Merge branch 'cms-ui-preparation'

This commit is contained in:
Ingo Schommer 2011-04-28 22:48:02 +12:00
commit 79e0634537
121 changed files with 3396 additions and 25876 deletions

1
admin/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.sass-cache

View File

@ -169,7 +169,8 @@ class LeftAndMain extends Controller {
$htmlEditorConfig = HtmlEditorConfig::get_active(); $htmlEditorConfig = HtmlEditorConfig::get_active();
$htmlEditorConfig->setOption('language', i18n::get_tinymce_lang()); $htmlEditorConfig->setOption('language', i18n::get_tinymce_lang());
if(!$htmlEditorConfig->getOption('content_css')) { if(!$htmlEditorConfig->getOption('content_css')) {
$cssFiles = 'sapphire/admin/css/editor.css'; $cssFiles = array();
$cssFiles[] = 'sapphire/admin/css/editor.css';
// Use theme from the site config // Use theme from the site config
if(class_exists('SiteConfig') && ($config = SiteConfig::current_site_config()) && $config->Theme) { if(class_exists('SiteConfig') && ($config = SiteConfig::current_site_config()) && $config->Theme) {
@ -180,58 +181,50 @@ class LeftAndMain extends Controller {
$theme = false; $theme = false;
} }
if($theme) $cssFiles .= ',' . THEMES_DIR . "/{$theme}/css/editor.css"; if($theme) $cssFiles[] = THEMES_DIR . "/{$theme}/css/editor.css";
else if(project()) $cssFiles .= ',' . project() . '/css/editor.css'; else if(project()) $cssFiles[] = project() . '/css/editor.css';
$htmlEditorConfig->setOption('content_css', $cssFiles); // Remove files that don't exist
foreach($cssFiles as $k => $cssFile) {
if(!file_exists(BASE_PATH . '/' . $cssFile)) unset($cssFiles[$k]);
} }
$htmlEditorConfig->setOption('content_css', implode(',', $cssFiles));
}
Requirements::css(SAPPHIRE_ADMIN_DIR . '/css/typography.css'); Requirements::css(SAPPHIRE_ADMIN_DIR . '/css/screen.css');
Requirements::css(SAPPHIRE_ADMIN_DIR . '/css/layout.css');
Requirements::css(SAPPHIRE_ADMIN_DIR . '/css/cms_left.css');
Requirements::css(SAPPHIRE_ADMIN_DIR . '/css/cms_right.css');
Requirements::css(SAPPHIRE_DIR . '/css/Form.css');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/prototypefix/intro.js'); Requirements::javascript(THIRDPARTY_DIR . '/prototype/prototype.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/prototype/prototype.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/prototypefix/outro.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery/jquery.js'); Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/jquery_improvements.js'); Requirements::javascript(SAPPHIRE_DIR . '/javascript/jquery_improvements.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/jquery-ui.js'); //import all of jquery ui Requirements::javascript(THIRDPARTY_DIR . '/jquery-ui/jquery-ui.js'); //import all of jquery ui
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/thirdparty/jquery-layout/jquery.layout.js'); Requirements::javascript(THIRDPARTY_DIR . '/json-js/json2.js');
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/thirdparty/jquery-layout/jquery.layout.state.js'); Requirements::javascript(THIRDPARTY_DIR . '/jquery-metadata/jquery.metadata.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/json-js/json2.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-metadata/jquery.metadata.js'); // entwine
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/jquery-fitheighttoparent/jquery.fitheighttoparent.js'); Requirements::javascript(THIRDPARTY_DIR . '/jquery-entwine/dist/jquery.entwine-dist.js');
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/ssui.core.js'); Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/ssui.core.js');
// @todo Load separately so the CSS files can be inlined // @todo Load separately so the CSS files can be inlined
Requirements::css(SAPPHIRE_DIR . '/thirdparty/jquery-ui-themes/smoothness/jquery.ui.all.css'); Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css');
// entwine
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-entwine/dist/jquery.entwine-dist.js');
// Required for TreeTools panel above tree // Required for TreeTools panel above tree
Requirements::javascript(SAPPHIRE_DIR . '/javascript/TabSet.js'); Requirements::javascript(SAPPHIRE_DIR . '/javascript/TabSet.js');
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/thirdparty/jsizes/lib/jquery.sizes.js');
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/thirdparty/jlayout/lib/jlayout.border.js');
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/thirdparty/jlayout/lib/jquery.jlayout.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/behaviour/behaviour.js'); Requirements::javascript(THIRDPARTY_DIR . '/behaviour/behaviour.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-cookie/jquery.cookie.js'); Requirements::javascript(THIRDPARTY_DIR . '/jquery-cookie/jquery.cookie.js');
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/thirdparty/jquery-notice/jquery.notice.js'); Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/thirdparty/jquery-notice/jquery.notice.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/jquery-ondemand/jquery.ondemand.js'); Requirements::javascript(SAPPHIRE_DIR . '/javascript/jquery-ondemand/jquery.ondemand.js');
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/jquery-changetracker/lib/jquery.changetracker.js'); Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/jquery-changetracker/lib/jquery.changetracker.js');
Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang'); Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang');
Requirements::add_i18n_javascript(SAPPHIRE_ADMIN_DIR . '/javascript/lang'); Requirements::add_i18n_javascript(SAPPHIRE_ADMIN_DIR . '/javascript/lang');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/scriptaculous/effects.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/scriptaculous/dragdrop.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/scriptaculous/controls.js');
Requirements::javascript(THIRDPARTY_DIR . '/tree/tree.js');
Requirements::css(THIRDPARTY_DIR . '/tree/tree.css');
Requirements::javascript(THIRDPARTY_DIR . '/jstree/jquery.jstree.js'); Requirements::javascript(THIRDPARTY_DIR . '/jstree/jquery.jstree.js');
Requirements::css(THIRDPARTY_DIR . '/jstree/themes/apple/style.css'); Requirements::css(THIRDPARTY_DIR . '/jstree/themes/apple/style.css');
@ -239,6 +232,7 @@ class LeftAndMain extends Controller {
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/LeftAndMain.Tree.js'); Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/LeftAndMain.Tree.js');
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/LeftAndMain.EditForm.js'); Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/LeftAndMain.EditForm.js');
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/LeftAndMain.AddForm.js'); Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/LeftAndMain.AddForm.js');
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/LeftAndMain.Preview.js');
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/LeftAndMain.BatchActions.js'); Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/LeftAndMain.BatchActions.js');
Requirements::themedCSS('typography'); Requirements::themedCSS('typography');
@ -259,27 +253,42 @@ class LeftAndMain extends Controller {
// Javascript combined files // Javascript combined files
Requirements::combine_files( Requirements::combine_files(
'base.js', 'lib.js',
array( array(
'sapphire/thirdparty/prototype/prototype.js', THIRDPARTY_DIR . '/prototype/prototype.js',
'sapphire/thirdparty/behaviour/behaviour.js', THIRDPARTY_DIR . '/behaviour/behaviour.js',
'sapphire/thirdparty/jquery/jquery.js', THIRDPARTY_DIR . '/jquery/jquery.js',
'sapphire/thirdparty/jquery-livequery/jquery.livequery.js', SAPPHIRE_DIR . '/javascript/jquery_improvements.js',
'sapphire/javascript/jquery-ondemand/jquery.ondemand.js', THIRDPARTY_DIR . '/jquery-livequery/jquery.livequery.js',
'sapphire/thirdparty/jquery-ui/jquery-ui.js', SAPPHIRE_DIR . '/javascript/jquery-ondemand/jquery.ondemand.js',
'sapphire/javascript/i18n.js', THIRDPARTY_DIR . '/jquery-ui/jquery-ui.js',
THIRDPARTY_DIR . '/json-js/json2.js',
THIRDPARTY_DIR . '/jquery-entwine/dist/jquery.entwine-dist.js',
THIRDPARTY_DIR . '/jquery-cookie/jquery.cookie.js',
SAPPHIRE_ADMIN_DIR . '/thirdparty/jquery-notice/jquery.notice.js',
THIRDPARTY_DIR . '/jquery-metadata/jquery.metadata.js',
SAPPHIRE_ADMIN_DIR . '/thirdparty/jsizes/lib/jquery.sizes.js',
SAPPHIRE_ADMIN_DIR . '/thirdparty/jlayout/lib/jlayout.border.js',
SAPPHIRE_ADMIN_DIR . '/thirdparty/jlayout/lib/jquery.jlayout.js',
THIRDPARTY_DIR . '/jstree/jquery.jstree.js',
SAPPHIRE_ADMIN_DIR . '/javascript/jquery-changetracker/lib/jquery.changetracker.js',
SAPPHIRE_DIR . '/javascript/TreeDropdownField.js',
SAPPHIRE_DIR . '/javascript/TabSet.js',
SAPPHIRE_DIR . '/javascript/Validator.js',
SAPPHIRE_DIR . '/javascript/i18n.js',
SAPPHIRE_ADMIN_DIR . '/javascript/ssui.core.js',
) )
); );
Requirements::combine_files( Requirements::combine_files(
'leftandmain.js', 'leftandmain.js',
array( array(
'sapphire/thirdparty/scriptaculous/effects.js', SAPPHIRE_ADMIN_DIR . '/javascript/LeftAndMain.js',
'sapphire/thirdparty/scriptaculous/dragdrop.js', SAPPHIRE_ADMIN_DIR . '/javascript/LeftAndMain.Tree.js',
'sapphire/thirdparty/scriptaculous/controls.js', SAPPHIRE_ADMIN_DIR . '/javascript/LeftAndMain.EditForm.js',
'sapphire/admin/javascript/LeftAndMain.js', SAPPHIRE_ADMIN_DIR . '/javascript/LeftAndMain.AddForm.js',
'sapphire/javascript/tree/tree.js', SAPPHIRE_ADMIN_DIR . '/javascript/LeftAndMain.Preview.js',
'sapphire/javascript/TreeDropdownField.js', SAPPHIRE_ADMIN_DIR . '/javascript/LeftAndMain.BatchActions.js',
) )
); );
@ -390,7 +399,9 @@ class LeftAndMain extends Controller {
$linkingmode = ""; $linkingmode = "";
if(strpos($this->Link(), $menuItem->url) !== false) { if($menuItem->controller && $this instanceof $menuItem->controller) {
$linkingmode = "current";
} else if(strpos($this->Link(), $menuItem->url) !== false) {
if($this->Link() == $menuItem->url) { if($this->Link() == $menuItem->url) {
$linkingmode = "current"; $linkingmode = "current";
@ -428,8 +439,8 @@ class LeftAndMain extends Controller {
return $menu; return $menu;
} }
public function CMSTopMenu() { public function Menu() {
return $this->renderWith(array('CMSTopMenu_alternative','CMSTopMenu')); return $this->renderWith($this->getTemplatesWithSuffix('_Menu'));
} }
/** /**
@ -444,17 +455,13 @@ class LeftAndMain extends Controller {
return $templates; return $templates;
} }
public function Left() { public function Content() {
return $this->renderWith($this->getTemplatesWithSuffix('_left')); return $this->renderWith($this->getTemplatesWithSuffix('_Content'));
}
public function Right() {
return $this->renderWith($this->getTemplatesWithSuffix('_right'));
} }
public function getRecord($id) { public function getRecord($id) {
$className = $this->stat('tree_class'); $className = $this->stat('tree_class');
if($id instanceof $className) { if($className && $id instanceof $className) {
return $id; return $id;
} else if(is_numeric($id)) { } else if(is_numeric($id)) {
return DataObject::get_by_id($className, $id); return DataObject::get_by_id($className, $id);
@ -486,7 +493,8 @@ class LeftAndMain extends Controller {
if (!$numChildrenMethod) $numChildrenMethod = 'numChildren'; if (!$numChildrenMethod) $numChildrenMethod = 'numChildren';
// Get the tree root // Get the tree root
$obj = $rootID ? $this->getRecord($rootID) : singleton($className); $record = ($rootID) ? $this->getRecord($rootID) : null;
$obj = $record ? $record : singleton($className);
// Mark the nodes of the tree to return // Mark the nodes of the tree to return
if ($filterFunction) $obj->setMarkingFilterFunction($filterFunction); if ($filterFunction) $obj->setMarkingFilterFunction($filterFunction);
@ -535,7 +543,7 @@ class LeftAndMain extends Controller {
$treeTitle = '...'; $treeTitle = '...';
} }
$html = "<ul id=\"sitetree\" class=\"tree unformatted\"><li id=\"record-0\" data-id=\"0\"class=\"Root nodelete\"><a href=\"$rootLink\"><strong>$treeTitle</strong></a>" $html = "<ul><li id=\"record-0\" data-id=\"0\"class=\"Root nodelete\"><a href=\"$rootLink\"><strong>$treeTitle</strong></a>"
. $html . "</li></ul>"; . $html . "</li></ul>";
} }
@ -602,6 +610,21 @@ class LeftAndMain extends Controller {
return $form->formHtmlContent(); return $form->formHtmlContent();
} }
public function delete($data, $form) {
$className = $this->stat('tree_class');
$record = DataObject::get_by_id($className, Convert::raw2sql($data['ID']));
if($record && !$record->canDelete()) return Security::permissionFailure();
$record->delete();
if($this->isAjax()) {
return $this->EmptyForm()->formHtmlContent();
} else {
$this->redirectBack();
}
}
/** /**
* Update the position and parent of a tree node. * Update the position and parent of a tree node.
* Only saves the node if changes were made. * Only saves the node if changes were made.
@ -719,7 +742,14 @@ class LeftAndMain extends Controller {
return $this->getEditForm(); return $this->getEditForm();
} }
public function getEditForm($id = null) { /**
* Calls {@link SiteTree->getCMSFields()}
*
* @param Int $id
* @param FieldSet $fields
* @return Form
*/
public function getEditForm($id = null, $fields = null) {
if(!$id) $id = $this->currentPageID(); if(!$id) $id = $this->currentPageID();
if(is_object($id)) { if(is_object($id)) {
@ -730,7 +760,7 @@ class LeftAndMain extends Controller {
} }
if($record) { if($record) {
$fields = $record->getCMSFields(); $fields = ($fields) ? $fields : $record->getCMSFields();
if ($fields == null) { if ($fields == null) {
user_error( user_error(
"getCMSFields() returned null - it should return a FieldSet object. "getCMSFields() returned null - it should return a FieldSet object.
@ -757,13 +787,19 @@ class LeftAndMain extends Controller {
$actions = $record->getCMSActions(); $actions = $record->getCMSActions();
// add default actions if none are defined // add default actions if none are defined
if(!$actions || !$actions->Count()) { if(!$actions || !$actions->Count()) {
if($record->canEdit()) { if($record->hasMethod('canDelete') && $record->canDelete()) {
$actions->push(new FormAction('save',_t('CMSMain.SAVE','Save'))); $actions->push($deleteAction = new FormAction('delete',_t('ModelAdmin.DELETE','Delete')));
$deleteAction->addExtraClass('ss-ui-action-destructive');
}
if($record->hasMethod('canEdit') && $record->canEdit()) {
$actions->push($saveAction = new FormAction('save',_t('CMSMain.SAVE','Save')));
$saveAction->addExtraClass('ss-ui-action-constructive');
} }
} }
} }
$form = new Form($this, "EditForm", $fields, $actions); $form = new Form($this, "EditForm", $fields, $actions);
$form->addExtraClass('cms-edit-form');
$form->loadDataFrom($record); $form->loadDataFrom($record);
// Add a default or custom validator. // Add a default or custom validator.
@ -786,7 +822,7 @@ class LeftAndMain extends Controller {
$form->unsetValidator(); $form->unsetValidator();
} }
if(!$record->canEdit()) { if($record->hasMethod('canEdit') && !$record->canEdit()) {
$readonlyFields = $form->Fields()->makeReadonly(); $readonlyFields = $form->Fields()->makeReadonly();
$form->setFields($readonlyFields); $form->setFields($readonlyFields);
} }
@ -812,22 +848,25 @@ class LeftAndMain extends Controller {
$this, $this,
"EditForm", "EditForm",
new FieldSet( new FieldSet(
new HeaderField( // new HeaderField(
'WelcomeHeader', // 'WelcomeHeader',
$this->getApplicationName() // $this->getApplicationName()
), // ),
new LiteralField( // new LiteralField(
'WelcomeText', // 'WelcomeText',
sprintf('<p id="WelcomeMessage">%s %s. %s</p>', // sprintf('<p id="WelcomeMessage">%s %s. %s</p>',
_t('LeftAndMain_right.ss.WELCOMETO','Welcome to'), // _t('LeftAndMain_right.ss.WELCOMETO','Welcome to'),
$this->getApplicationName(), // $this->getApplicationName(),
_t('CHOOSEPAGE','Please choose an item from the left.') // _t('CHOOSEPAGE','Please choose an item from the left.')
) // )
) // )
), ),
new FieldSet() new FieldSet()
); );
$form->unsetValidator(); $form->unsetValidator();
$form->addExtraClass('cms-edit-form');
$form->addExtraClass('root-form');
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
return $form; return $form;
} }
@ -839,19 +878,18 @@ class LeftAndMain extends Controller {
$class = $this->stat('tree_class'); $class = $this->stat('tree_class');
$typeMap = array($class => singleton($class)->i18n_singular_name()); $typeMap = array($class => singleton($class)->i18n_singular_name());
$typeField = new DropdownField('Type', false, $typeMap, $class);
$form = new Form( $form = new Form(
$this, $this,
'AddForm', 'AddForm',
new FieldSet( new FieldSet(
new HiddenField('ParentID'), new HiddenField('ParentID')
$typeField->performReadonlyTransformation()
), ),
new FieldSet( new FieldSet(
new FormAction('doAdd', _t('AssetAdmin_left.ss.GO','Go')) $addAction = new FormAction('doAdd', _t('AssetAdmin_left.ss.GO','Go'))
) )
); );
$form->addExtraClass('actionparams'); $addAction->addExtraClass('ss-ui-action-constructive');
$form->addExtraClass('add-form');
return $form; return $form;
} }
@ -905,22 +943,13 @@ class LeftAndMain extends Controller {
*/ */
function BatchActionsForm() { function BatchActionsForm() {
$actions = $this->batchactions()->batchActionList(); $actions = $this->batchactions()->batchActionList();
$actionsMap = array(); $actionsMap = array('-1' => _t('LeftAndMain.DropdownBatchActionsDefault', 'Actions'));
foreach($actions as $action) $actionsMap[$action->Link] = $action->Title; foreach($actions as $action) $actionsMap[$action->Link] = $action->Title;
$form = new Form( $form = new Form(
$this, $this,
'BatchActionsForm', 'BatchActionsForm',
new FieldSet( new FieldSet(
new LiteralField(
'Intro',
sprintf('<p><small>%s</small></p>',
_t(
'CMSMain_left.ss.SELECTPAGESACTIONS',
'Select the pages that you want to change &amp; then click an action:'
)
)
),
new HiddenField('csvIDs'), new HiddenField('csvIDs'),
new DropdownField( new DropdownField(
'Action', 'Action',
@ -933,7 +962,7 @@ class LeftAndMain extends Controller {
new FormAction('submit', "Go") new FormAction('submit', "Go")
) )
); );
$form->addExtraClass('actionparams'); $form->addExtraClass('cms-batch-actions nostyle');
$form->unsetValidator(); $form->unsetValidator();
return $form; return $form;
@ -942,6 +971,7 @@ class LeftAndMain extends Controller {
public function myprofile() { public function myprofile() {
$form = $this->Member_ProfileForm(); $form = $this->Member_ProfileForm();
return $this->customise(array( return $this->customise(array(
'Content' => ' ',
'Form' => $form 'Form' => $form
))->renderWith('BlankPage'); ))->renderWith('BlankPage');
} }
@ -1062,6 +1092,13 @@ class LeftAndMain extends Controller {
} }
} }
/**
* @return SiteConfig
*/
function SiteConfig() {
return SiteConfig::current_site_config();
}
/** /**
* The application name. Customisable by calling * The application name. Customisable by calling
* LeftAndMain::setApplicationName() - the first parameter. * LeftAndMain::setApplicationName() - the first parameter.
@ -1070,31 +1107,11 @@ class LeftAndMain extends Controller {
*/ */
static $application_name = 'SilverStripe CMS'; static $application_name = 'SilverStripe CMS';
/**
* The application logo text. Customisable by calling
* LeftAndMain::setApplicationName() - the second parameter.
*
* @var String
*/
static $application_logo_text = 'SilverStripe';
/**
* Set the application name, and the logo text.
*
* @param String $name The application name
* @param String $logoText The logo text
*/
static $application_link = "http://www.silverstripe.org/";
/** /**
* @param String $name * @param String $name
* @param String $logoText
* @param String $link (Optional)
*/ */
static function setApplicationName($name, $logoText = null, $link = null) { static function setApplicationName($name) {
self::$application_name = $name; self::$application_name = $name;
self::$application_logo_text = $logoText ? $logoText : $name;
if($link) self::$application_link = $link;
} }
/** /**
@ -1105,21 +1122,6 @@ class LeftAndMain extends Controller {
return self::$application_name; return self::$application_name;
} }
/**
* Get the application logo text.
* @return String
*/
function getApplicationLogoText() {
return self::$application_logo_text;
}
/**
* @return String
*/
function ApplicationLink() {
return self::$application_link;
}
/** /**
* Return the title of the current section, as shown on the main menu * Return the title of the current section, as shown on the main menu
*/ */
@ -1132,65 +1134,6 @@ class LeftAndMain extends Controller {
} }
} }
/**
* The application logo path. Customisable by calling
* LeftAndMain::setLogo() - the first parameter.
*
* @var unknown_type
*/
static $application_logo = 'sapphire/admin/images/mainmenu/logo.gif';
/**
* The application logo style. Customisable by calling
* LeftAndMain::setLogo() - the second parameter.
*
* @var String
*/
static $application_logo_style = '';
/**
* Set the CMS application logo.
*
* @param String $logo Relative path to the logo
* @param String $logoStyle Custom CSS styles for the logo
* e.g. "border: 1px solid red; padding: 5px;"
*/
static function setLogo($logo, $logoStyle) {
self::$application_logo = $logo;
self::$application_logo_style = $logoStyle;
self::$application_logo_text = '';
}
/**
* The height of the image should be around 164px to avoid the overlaping between the image and loading animation graphic.
* If the given image's height is significantly larger or smaller, adjust the loading animation's top offset in
* positionLoadingSpinner() in LeftAndMain.js
*/
protected static $loading_image = 'sapphire/admin/images/logo.gif';
/**
* Set the image shown when the CMS is loading.
*/
static function set_loading_image($loadingImage) {
self::$loading_image = $loadingImage;
}
function LoadingImage() {
return self::$loading_image;
}
/**
* Combines an optional background image and additional CSS styles,
* set through {@link setLogo()}.
*
* @return String CSS attribute
*/
function LogoStyle() {
$attr = self::$application_logo_style;
if(self::$application_logo) $attr .= "background: url(" . self::$application_logo . ") no-repeat; ";
return $attr;
}
/** /**
* Return the base directory of the tiny_mce codebase * Return the base directory of the tiny_mce codebase
*/ */

View File

@ -147,7 +147,6 @@ abstract class ModelAdmin extends LeftAndMain {
//user_error('ModelAdmin::init(): Invalid Model class', E_USER_ERROR); //user_error('ModelAdmin::init(): Invalid Model class', E_USER_ERROR);
} }
Requirements::css(SAPPHIRE_ADMIN_DIR . '/css/ModelAdmin.css'); // standard layout formatting for management UI
Requirements::css(SAPPHIRE_ADMIN_DIR . '/css/silverstripe.tabs.css'); // follows the jQuery UI theme conventions Requirements::css(SAPPHIRE_ADMIN_DIR . '/css/silverstripe.tabs.css'); // follows the jQuery UI theme conventions
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery/jquery.js'); Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery/jquery.js');
@ -414,7 +413,7 @@ class ModelAdmin_CollectionController extends Controller {
$form->setHTMLID("Form_SearchForm_" . $this->modelClass); $form->setHTMLID("Form_SearchForm_" . $this->modelClass);
$form->disableSecurityToken(); $form->disableSecurityToken();
$clearAction->useButtonTag = true; $clearAction->useButtonTag = true;
$clearAction->addExtraClass('minorAction'); $clearAction->addExtraClass('ss-ui-action-minor');
return $form; return $form;
} }
@ -439,7 +438,7 @@ class ModelAdmin_CollectionController extends Controller {
new FieldSet($createButton = new FormAction('add', $buttonLabel)), new FieldSet($createButton = new FormAction('add', $buttonLabel)),
$validator = new RequiredFields() $validator = new RequiredFields()
); );
$createButton->addExtraClass('ss-ui-action-constructive');
$createButton->dontEscape = true; $createButton->dontEscape = true;
$validator->setJavascriptValidationHandler('none'); $validator->setJavascriptValidationHandler('none');
$form->setHTMLID("Form_CreateForm_" . $this->modelClass); $form->setHTMLID("Form_CreateForm_" . $this->modelClass);
@ -768,12 +767,8 @@ class ModelAdmin_CollectionController extends Controller {
$this, $this,
'ResultsForm', 'ResultsForm',
new FieldSet( new FieldSet(
new TabSet('Root', new HeaderField('SearchResults', _t('ModelAdmin.SEARCHRESULTS','Search Results'), 2),
new Tab('SearchResults',
_t('ModelAdmin.SEARCHRESULTS','Search Results'),
$tf $tf
)
)
), ),
new FieldSet() new FieldSet()
); );
@ -952,7 +947,8 @@ class ModelAdmin_RecordController extends Controller {
$actions = $this->currentRecord->getCMSActions(); $actions = $this->currentRecord->getCMSActions();
if($this->currentRecord->canEdit(Member::currentUser())){ if($this->currentRecord->canEdit(Member::currentUser())){
if(!$actions->fieldByName('action_doSave') && !$actions->fieldByName('action_save')) { if(!$actions->fieldByName('action_doSave') && !$actions->fieldByName('action_save')) {
$actions->push(new FormAction("doSave", _t('ModelAdmin.SAVE', "Save"))); $actions->push($saveAction = new FormAction("doSave", _t('ModelAdmin.SAVE', "Save")));
$saveAction->addExtraClass('ss-ui-action-constructive');
} }
}else{ }else{
$fields = $fields->makeReadonly(); $fields = $fields->makeReadonly();
@ -962,7 +958,7 @@ class ModelAdmin_RecordController extends Controller {
if(!$actions->fieldByName('action_doDelete')) { if(!$actions->fieldByName('action_doDelete')) {
$actions->insertFirst($deleteAction = new FormAction('doDelete', _t('ModelAdmin.DELETE', 'Delete'))); $actions->insertFirst($deleteAction = new FormAction('doDelete', _t('ModelAdmin.DELETE', 'Delete')));
} }
$deleteAction->addExtraClass('delete'); $deleteAction->addExtraClass('delete ss-ui-action-destructive');
} }
$form = new Form($this, "EditForm", $fields, $actions, $validator); $form = new Form($this, "EditForm", $fields, $actions, $validator);

View File

@ -10,7 +10,7 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
static $url_rule = '/$Action/$ID/$OtherID'; static $url_rule = '/$Action/$ID/$OtherID';
static $menu_title = 'Security'; static $menu_title = 'Users';
static $tree_class = 'Group'; static $tree_class = 'Group';
@ -36,8 +36,7 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
public function init() { public function init() {
parent::init(); parent::init();
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/SecurityAdmin_left.js'); Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/SecurityAdmin.js');
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/SecurityAdmin_right.js');
} }
function getEditForm($id = null) { function getEditForm($id = null) {
@ -169,6 +168,14 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
$fields, $fields,
$actions $actions
); );
$form->addExtraClass('cms-edit-form');
return $form;
}
function AddForm() {
$form = parent::AddForm();
$form->Actions()->fieldByName('action_doAdd')->setTitle(_t('AssetAdmin.ActionAdd', 'Add folder'));
return $form; return $form;
} }

13
admin/config.rb Normal file
View File

@ -0,0 +1,13 @@
# Require any additional compass plugins here.
require 'compass-colors'
project_type = :stand_alone
# Set this to the root of your project when deployed:
http_path = "/"
css_dir = "css"
sass_dir = "scss"
images_dir = "images"
javascripts_dir = "javascript"
output_style = :compact
# To enable relative paths to assets via compass helper functions. Uncomment:
relative_assets = true

View File

@ -555,14 +555,6 @@
margin-left: 0; margin-left: 0;
} }
/**
* jQuery UI Datepicker - necessary because of custom
* z-indexing performed in jQuery.layout
*/
#ui-datepicker-div {
z-index: 1002;
}
/** /**
* Upload Image (Image Toolbar) * Upload Image (Image Toolbar)
*/ */

View File

@ -5,15 +5,6 @@
font-family: Arial, Helvetica, sans-serif; font-family: Arial, Helvetica, sans-serif;
} }
html {
overflow: hidden;
}
body {
background: #ccdef3;
height: 100%;
}
.ss-loading-screen, .ss-loading-screen .loading-logo { .ss-loading-screen, .ss-loading-screen .loading-logo {
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -28,6 +19,7 @@ body {
} }
.ss-loading-screen .loading-logo { .ss-loading-screen .loading-logo {
background-url: url(../images/logo.gif);
background-repeat: no-repeat; background-repeat: no-repeat;
background-color: transparent; background-color: transparent;
background-position: 50% 50%; background-position: 50% 50%;
@ -85,8 +77,6 @@ body {
*/ */
.ui-layout-pane { /* all 'panes' */ .ui-layout-pane { /* all 'panes' */
background: #FFF; background: #FFF;
/*border: 1px solid #BBB;*/
overflow: auto;
} }
/* Overflow is handled by tabsets inside the panel */ /* Overflow is handled by tabsets inside the panel */

443
admin/css/screen.css Normal file
View File

@ -0,0 +1,443 @@
@charset "UTF-8";
/** This file is the central collection of included modules, links to custom SCSS files, and any global SCSS variable definitions. DO NOT ADD stylesheet rules to this file directly! Note: By prefixing files with an underscore, they won't create individual CSS files. */
/* line 14, ../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.10.6/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { margin: 0; padding: 0; border: 0; outline: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; }
/* line 17, ../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.10.6/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
body { line-height: 1; color: black; background: white; }
/* line 19, ../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.10.6/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
ol, ul { list-style: none; }
/* line 21, ../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.10.6/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
table { border-collapse: separate; border-spacing: 0; vertical-align: middle; }
/* line 23, ../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.10.6/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
caption, th, td { text-align: left; font-weight: normal; vertical-align: middle; }
/* line 25, ../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.10.6/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
q, blockquote { quotes: "" ""; }
/* line 96, ../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.10.6/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
q:before, q:after, blockquote:before, blockquote:after { content: ""; }
/* line 27, ../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.10.6/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
a img { border: none; }
/** This file only contains color definitions. Please put any further formatting into _style.scss. */
/** This file defines the structural layout of the CMS interface. Ideally, you should be able to lay out the base elements of the CMS with only this file. Please put any presentational definitions (color, fonts, backgrounds, paddings) into _style.scss or _uitheme.scss instead. */
/* line 9, ../scss/_layout.scss */
html, body { width: 100%; height: 100%; padding: 0; margin: 0; overflow: hidden; }
/* line 17, ../scss/_layout.scss */
.cms-preview { width: 1px; overflow: hidden; }
/* line 21, ../scss/_layout.scss */
.cms-preview .cms-preview-toggle { width: 10px; }
/* line 25, ../scss/_layout.scss */
.cms-preview iframe { width: 100%; height: 100%; }
/* line 31, ../scss/_layout.scss */
.cms-container { height: 100%; }
/* line 35, ../scss/_layout.scss */
.cms-menu { width: 250px; overflow: auto; }
/* line 47, ../scss/_layout.scss */
.cms-menu, .cms-content, .cms-content-header, .cms-content-tools, .cms-content-form { display: -moz-inline-box; -moz-box-orient: vertical; display: inline-block; vertical-align: middle; *vertical-align: auto; }
/* line 7, ../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.10.6/frameworks/compass/stylesheets/compass/css3/_inline-block.scss */
.cms-menu, .cms-content, .cms-content-header, .cms-content-tools, .cms-content-form { *display: inline; }
/* line 51, ../scss/_layout.scss */
.cms-content-tools { width: 250px; overflow: auto; }
/* line 56, ../scss/_layout.scss */
.cms-content-form { overflow: auto; }
/* line 60, ../scss/_layout.scss */
.cms-content-header { height: 40px; }
/* line 63, ../scss/_layout.scss */
.cms-content-header h2 { width: 226px; }
/* line 67, ../scss/_layout.scss */
.cms-content-header > div { width: 9999em; overflow: hidden; }
/* line 73, ../scss/_layout.scss */
.cms-content-actions { padding: 10px; }
/* line 77, ../scss/_layout.scss */
.cms-logo { height: 30px; overflow: hidden; vertical-align: middle; }
/* line 83, ../scss/_layout.scss */
.cms-login-status { height: 30px; overflow: hidden; vertical-align: middle; }
/** This file defines the jstree base styling (see http://jstree.com), as well as any customizations (see bottom of file). The styles are usually added through jstree.js on DOM load, but we need it earlier in order to correctly display the uninitialized tree. */
/* line 10, ../scss/_tree.scss */
.jstree ul { display: block; margin: 0 0 0 0; padding: 0 0 0 0; list-style-type: none; }
/* line 16, ../scss/_tree.scss */
.jstree li { display: block; margin: 0 0 0 0; padding: 0 0 0 0; list-style-type: none; display: block; min-height: 18px; line-height: 18px; white-space: nowrap; margin-left: 18px; min-width: 18px; }
/* line 28, ../scss/_tree.scss */
.jstree ins { display: inline-block; text-decoration: none; width: 18px; height: 18px; margin: 0 0 0 0; padding: 0; }
/* line 36, ../scss/_tree.scss */
.jstree a { display: inline-block; line-height: 16px; height: 16px; color: black; white-space: nowrap; text-decoration: none; padding: 1px 2px; margin: 0; }
/* line 45, ../scss/_tree.scss */
.jstree a:focus { outline: none; }
/* line 49, ../scss/_tree.scss */
.jstree a > ins { height: 16px; width: 16px; }
/* line 53, ../scss/_tree.scss */
.jstree a > .jstree-icon { margin-right: 3px; }
/* line 61, ../scss/_tree.scss */
.jstree li.jstree-open > ul { display: block; }
/* line 64, ../scss/_tree.scss */
.jstree li.jstree-closed > ul { display: none; }
/* line 69, ../scss/_tree.scss */
.jstree li.disabled a { color: #aaaaaa; }
/* line 76, ../scss/_tree.scss */
.jstree-rtl a > .jstree-icon { margin-left: 3px; margin-right: 0; }
/* line 80, ../scss/_tree.scss */
.jstree-rtl li { margin-left: 0; margin-right: 18px; }
/* line 87, ../scss/_tree.scss */
.jstree-rtl > ul > li { margin-right: 0px; }
/* line 91, ../scss/_tree.scss */
.jstree > ul > li { margin-left: 0px; }
/* line 95, ../scss/_tree.scss */
#vakata-dragged { display: block; margin: 0 0 0 0; padding: 4px 4px 4px 24px; position: absolute; top: -2000px; line-height: 16px; z-index: 10000; }
/* line 104, ../scss/_tree.scss */
#vakata-contextmenu { display: block; visibility: hidden; left: 0; top: -200px; position: absolute; margin: 0; padding: 0; min-width: 180px; background: #ebebeb; border: 1px solid silver; z-index: 10000; *width: 180px; }
/* line 117, ../scss/_tree.scss */
#vakata-contextmenu ul { min-width: 180px; *width: 180px; }
/* line 120, ../scss/_tree.scss */
#vakata-contextmenu ul, #vakata-contextmenu li { margin: 0; padding: 0; list-style-type: none; display: block; }
/* line 126, ../scss/_tree.scss */
#vakata-contextmenu li { line-height: 20px; min-height: 20px; position: relative; padding: 0px; }
/* line 132, ../scss/_tree.scss */
#vakata-contextmenu li a { padding: 1px 6px; line-height: 17px; display: block; text-decoration: none; margin: 1px 1px 0 1px; }
/* line 139, ../scss/_tree.scss */
#vakata-contextmenu li ins { float: left; width: 16px; height: 16px; text-decoration: none; margin-right: 2px; }
/* line 146, ../scss/_tree.scss */
#vakata-contextmenu li a:hover, #vakata-contextmenu li.vakata-hover > a { background: gray; color: white; }
/* line 150, ../scss/_tree.scss */
#vakata-contextmenu li ul { display: none; position: absolute; top: -2px; left: 100%; background: #ebebeb; border: 1px solid gray; }
/* line 158, ../scss/_tree.scss */
#vakata-contextmenu .right { right: 100%; left: auto; }
/* line 162, ../scss/_tree.scss */
#vakata-contextmenu .bottom { bottom: -1px; top: auto; }
/* line 166, ../scss/_tree.scss */
#vakata-contextmenu li.vakata-separator { min-height: 0; height: 1px; line-height: 1px; font-size: 1px; overflow: hidden; margin: 0 2px; background: silver; /* border-top:1px solid #fefefe; */ padding: 0; }
/* line 176, ../scss/_tree.scss */
.jstree ul, .jstree li { display: block; margin: 0 0 0 0; padding: 0 0 0 0; list-style-type: none; }
/* line 182, ../scss/_tree.scss */
.jstree li { display: block; min-height: 18px; line-height: 18px; white-space: nowrap; margin-left: 18px; min-width: 18px; }
/* line 190, ../scss/_tree.scss */
.jstree-rtl li { margin-left: 0; margin-right: 18px; }
/* line 194, ../scss/_tree.scss */
.jstree > ul > li { margin-left: 0px; }
/* line 197, ../scss/_tree.scss */
.jstree-rtl > ul > li { margin-right: 0px; }
/* line 200, ../scss/_tree.scss */
.jstree ins { display: inline-block; text-decoration: none; width: 18px; height: 18px; margin: 0 0 0 0; padding: 0; }
/* line 208, ../scss/_tree.scss */
.jstree a { display: inline-block; line-height: 16px; height: 16px; color: black; white-space: nowrap; text-decoration: none; padding: 1px 2px; margin: 0; }
/* line 218, ../scss/_tree.scss */
.jstree a:focus { outline: none; }
/* line 221, ../scss/_tree.scss */
.jstree a > ins { height: 16px; width: 16px; }
/* line 225, ../scss/_tree.scss */
.jstree a > .jstree-icon { margin-right: 3px; }
/* line 228, ../scss/_tree.scss */
.jstree-rtl a > .jstree-icon { margin-left: 3px; margin-right: 0; }
/* line 232, ../scss/_tree.scss */
li.jstree-open > ul { display: block; }
/* line 235, ../scss/_tree.scss */
li.jstree-closed > ul { display: none; }
/* line 238, ../scss/_tree.scss */
#vakata-dragged ins { display: block; text-decoration: none; width: 16px; height: 16px; margin: 0 0 0 0; padding: 0; position: absolute; top: 4px; left: 4px; -moz-border-radius: 4px; border-radius: 4px; -webkit-border-radius: 4px; }
/* line 252, ../scss/_tree.scss */
#vakata-dragged .jstree-ok { background: green; }
/* line 255, ../scss/_tree.scss */
#vakata-dragged .jstree-invalid { background: red; }
/* line 258, ../scss/_tree.scss */
#jstree-marker { padding: 0; margin: 0; font-size: 12px; overflow: hidden; height: 12px; width: 8px; position: absolute; top: -30px; z-index: 10001; background-repeat: no-repeat; display: none; background-color: transparent; text-shadow: 1px 1px 1px white; color: black; line-height: 10px; }
/* line 275, ../scss/_tree.scss */
#jstree-marker-line { padding: 0; margin: 0; line-height: 0%; font-size: 1px; overflow: hidden; height: 1px; width: 100px; position: absolute; top: -30px; z-index: 10000; background-repeat: no-repeat; display: none; background-color: #456c43; cursor: pointer; border: 1px solid #eeeeee; border-left: 0; -moz-box-shadow: 0px 0px 2px #666; -webkit-box-shadow: 0px 0px 2px #666; box-shadow: 0px 0px 2px #666; -moz-border-radius: 1px; border-radius: 1px; -webkit-border-radius: 1px; }
/* line 299, ../scss/_tree.scss */
.jstree .jstree-real-checkbox { display: none; }
/* line 302, ../scss/_tree.scss */
.jstree-themeroller .ui-icon { overflow: visible; }
/* line 305, ../scss/_tree.scss */
.jstree-themeroller a { padding: 0 2px; }
/* line 308, ../scss/_tree.scss */
.jstree-themeroller .jstree-no-icon { display: none; }
/* line 311, ../scss/_tree.scss */
.jstree .jstree-wholerow-real { position: relative; z-index: 1; }
/* line 315, ../scss/_tree.scss */
.jstree .jstree-wholerow-real li { cursor: pointer; }
/* line 318, ../scss/_tree.scss */
.jstree .jstree-wholerow-real a { border-left-color: transparent !important; border-right-color: transparent !important; }
/* line 322, ../scss/_tree.scss */
.jstree .jstree-wholerow { position: relative; z-index: 0; height: 0; }
/* line 327, ../scss/_tree.scss */
.jstree .jstree-wholerow ul, .jstree .jstree-wholerow li { width: 100%; }
/* line 330, ../scss/_tree.scss */
.jstree .jstree-wholerow, .jstree .jstree-wholerow ul, .jstree .jstree-wholerow li, .jstree .jstree-wholerow a { margin: 0 !important; padding: 0 !important; }
/* line 334, ../scss/_tree.scss */
.jstree .jstree-wholerow, .jstree .jstree-wholerow ul, .jstree .jstree-wholerow li { background: transparent !important; }
/* line 337, ../scss/_tree.scss */
.jstree .jstree-wholerow ins, .jstree .jstree-wholerow span, .jstree .jstree-wholerow input { display: none !important; }
/* line 340, ../scss/_tree.scss */
.jstree .jstree-wholerow a, .jstree .jstree-wholerow a:hover { text-indent: -9999px !important; width: 100%; padding: 0 !important; border-right-width: 0px !important; border-left-width: 0px !important; }
/* line 347, ../scss/_tree.scss */
.jstree .jstree-wholerow-span { position: absolute; left: 0; margin: 0px; padding: 0; height: 18px; border-width: 0; padding: 0; z-index: 0; }
/* line 360, ../scss/_tree.scss */
.cms .jstree-apple.jstree-focused { background: none; }
/* line 363, ../scss/_tree.scss */
.cms .jstree-apple > ul { background: none; }
/* line 1, ../scss/_menu.scss */
.cms-menu { z-index: 10; background-color: #ebeff1; -moz-box-shadow: #aaaaaa 3px 0 3px 0; -webkit-box-shadow: #aaaaaa 3px 0 3px 0; -o-box-shadow: #aaaaaa 3px 0 3px 0; box-shadow: #aaaaaa 3px 0 3px 0; }
/* line 6, ../scss/_menu.scss */
.cms-menu a { text-decoration: none; }
/* line 12, ../scss/_menu.scss */
.cms-menu-list li { background-color: #b0bfc6; background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #b0bfc6), color-stop(100%, #758f9b)); background-image: -moz-linear-gradient(top, #b0bfc6 0%, #758f9b 100%); background-image: linear-gradient(top, #b0bfc6 0%, #758f9b 100%); border-bottom: 1px solid #aaaaaa; }
/* line 20, ../scss/_menu.scss */
.cms-menu-list li a { display: block; height: 32px; vertical-align: middle; font-size: 14px; text-shadow: #aaaaaa 1px 1px 1px; color: #333333; padding: 5px; }
/* line 29, ../scss/_menu.scss */
.cms-menu-list li a .icon { display: block; float: left; margin-right: 5px; background: url('../images/icons-32.png?1303890208') no-repeat; width: 32px; height: 32px; overflow: hidden; background-position: 0px 0px; }
/* line 37, ../scss/_menu.scss */
.cms-menu-list li a .text { display: block; padding-top: 10px; }
/* line 43, ../scss/_menu.scss */
.cms-menu-list li.current { background-color: #338dc1; background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #338dc1), color-stop(100%, #1e5270)); background-image: -moz-linear-gradient(top, #338dc1 0%, #1e5270 100%); background-image: linear-gradient(top, #338dc1 0%, #1e5270 100%); }
/* line 50, ../scss/_menu.scss */
.cms-menu-list li.current li { background-color: #2e7ead; background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #2e7ead), color-stop(100%, #287099)); background-image: -moz-linear-gradient(top, #2e7ead 0%, #287099 100%); background-image: linear-gradient(top, #2e7ead 0%, #287099 100%); }
/* line 58, ../scss/_menu.scss */
.cms-menu-list li.current a { color: white; text-shadow: #333333 1px 1px 1px; }
/* line 68, ../scss/_menu.scss */
.cms-menu-list li li:first { -moz-box-shadow: #333333 0 4px 4px 0; -webkit-box-shadow: #333333 0 4px 4px 0; -o-box-shadow: #333333 0 4px 4px 0; box-shadow: #333333 0 4px 4px 0; }
/* line 72, ../scss/_menu.scss */
.cms-menu-list li li a { font-size: 12px; text-shadow: #333333 1px 1px 1px; margin: 0; padding-left: 30px; color: white; }
/* line 80, ../scss/_menu.scss */
.cms-menu-list li li.current a { font-weight: bold; }
/* line 87, ../scss/_menu.scss */
.cms-menu-list li#Menu-CMSMain a .icon { background-position: 0px 0px; }
/* line 88, ../scss/_menu.scss */
.cms-menu-list li#Menu-CMSMain.current a .icon, .cms-menu-list li#Menu-CMSMain a:hover .icon { background-position: -32px 0px; }
/* line 89, ../scss/_menu.scss */
.cms-menu-list li#Menu-AssetAdmin a .icon { background-position: 0px -32px; }
/* line 90, ../scss/_menu.scss */
.cms-menu-list li#Menu-AssetAdmin.current a .icon, .cms-menu-list li#Menu-AssetAdmin a:hover .icon { background-position: -32px -32px; }
/** This file defines common styles for form elements used throughout the CMS interface. It is an addition to the base styles defined in sapphire/css/Form.css. */
/* line 9, ../scss/_forms.scss */
.cms .field { display: block; padding: 10px 0; border-bottom: 1px solid #aaaaaa; }
/* line 14, ../scss/_forms.scss */
.cms .field label { float: left; width: 10em; }
/* line 20, ../scss/_forms.scss */
.cms .field .middleColumn { margin-left: 10em; }
/* line 23, ../scss/_forms.scss */
.cms .field .middleColumn .field { display: inline; padding: 0; border: none; }
/* line 29, ../scss/_forms.scss */
.cms .field .middleColumn label { float: none; width: auto; }
/* line 38, ../scss/_forms.scss */
.cms form.nostyle .field { display: inline; padding: 0; border: 0; }
/* line 44, ../scss/_forms.scss */
.cms form.nostyle label { float: none; width: auto; }
/* line 49, ../scss/_forms.scss */
.cms form.nostyle .middleColumn { margin-left: 0; }
/* line 55, ../scss/_forms.scss */
.cms .field.nolabel .middleColumn { margin-left: 0; }
/* line 60, ../scss/_forms.scss */
.cms input, .cms textarea { -moz-border-radius: 5px; -webkit-border-radius: 5px; -o-border-radius: 5px; -ms-border-radius: 5px; -khtml-border-radius: 5px; border-radius: 5px; background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #ebeff1), color-stop(50%, #ffffff), color-stop(100%, #ebeff1)); background-image: -moz-linear-gradient(top, #ebeff1 0%, #ffffff 50%, #ebeff1 100%); background-image: linear-gradient(top, #ebeff1 0%, #ffffff 50%, #ebeff1 100%); border: 1px solid #aaaaaa; padding: 3px; }
/* line 71, ../scss/_forms.scss */
.cms input.loading { padding-left: 16px; background: #ebeff1 url(../../images/network-save.gif) no-repeat center left; }
/* line 79, ../scss/_forms.scss */
.cms .ss-ui-button.ss-ui-action-constructive, .cms .ui-widget-content .ss-ui-button.ss-ui-action-constructive, .cms .ui-widget-header .ss-ui-button.ss-ui-action-constructive { background: none; background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #77b53f), color-stop(100%, #456925)); background-image: -moz-linear-gradient(top, #77b53f 0%, #456925 100%); background-image: linear-gradient(top, #77b53f 0%, #456925 100%); color: white; }
/* line 90, ../scss/_forms.scss */
.cms .ss-ui-button.ss-ui-action-destructive, .cms .ui-widget-content .ss-ui-button.ss-ui-action-destructive, .cms .ui-widget-header .ss-ui-button.ss-ui-action-destructive { color: red; }
/* line 96, ../scss/_forms.scss */
.cms .ss-ui-button.ss-ui-action-minor, .cms .ui-widget-content .ss-ui-button.ss-ui-action-minor .ui-widget-header .ss-ui-button.ss-ui-action-minor { background: none; padding: 0; border: 0; color: #333333; text-decoration: underline; }
/* line 104, ../scss/_forms.scss */
.cms .cms-edit-form { padding-bottom: 20px; }
/* line 108, ../scss/_forms.scss */
.cms .cms-edit-form .Actions { text-align: right; }
/* line 115, ../scss/_forms.scss */
.cms .cms-content-tools .field label { float: none; width: auto; }
/* line 120, ../scss/_forms.scss */
.cms .cms-content-tools .field .middleColumn { margin-left: 0; }
/** This file defines CMS-specific customizations to the jQuery UI theme. Every rule in this file should be wrapped in the '.cms' selector (to make it more specific), and contain ONLY overwritten jQuery UI rules (with 'ui-' prefix). This file should be fairly short, as we're using our own custom jQuery UI theme already. TODO Add theme reference Use _style.scss to add more generic style information, and read the jQuery UI theming API: http://jqueryui.com/docs/Theming/API */
/* line 14, ../scss/_uitheme.scss */
.cms .ui-tabs { padding: 0; }
/* line 17, ../scss/_uitheme.scss */
.cms .ui-tabs .ui-widget-header { border: 0; background: none; }
/* line 24, ../scss/_uitheme.scss */
.cms .ui-widget-content, .cms .ui-tabs .ui-tabs-panel { color: #444444; font-size: 1em; border: 0; background: #ebeff1; }
/* line 31, ../scss/_uitheme.scss */
.cms .ui-widget-header { background: #ebeff1; border: 0; }
/* line 36, ../scss/_uitheme.scss */
.cms .ss-ui-button { padding: 5px; text-decoration: none; }
/** This file defines the 'theme' of the CMS: Colors, fonts, backgrounds, and detailed alignments. Together with _layout.css it provides the presentational backbone to the CMS. Ideally a developer should be able to exchange this file with his own theme easily. Please don't put any dimension, display or float information on major structural components like '.cms-container' or '.cms-header' in here, use the _layout.scss file instead. Use SCSS variable definitions in screen.css to avoid repeating styles like background colours or padding dimensions. See _colours.scss to get started. To avoid this file getting too large and complicated, it is encouraged to create new SCSS files for larger components like the CMS menu or tree (see _tree.scss and _menu.scss). */
/* line 18, ../scss/_style.scss */
body { font-size: 13px; font-family: Verdana, Arial, sans-serif; color: #444444; }
/* line 14, ../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.10.6/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
body html, body body, body div, body span, body applet, body object, body iframe, body h1, body h2, body h3, body h4, body h5, body h6, body p, body blockquote, body pre, body a, body abbr, body acronym, body address, body big, body cite, body code, body del, body dfn, body em, body font, body img, body ins, body kbd, body q, body s, body samp, body small, body strike, body strong, body sub, body sup, body tt, body var, body dl, body dt, body dd, body ol, body ul, body li, body fieldset, body form, body label, body legend, body table, body caption, body tbody, body tfoot, body thead, body tr, body th, body td { margin: 0; padding: 0; border: 0; outline: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; }
/* line 17, ../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.10.6/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
body body { line-height: 1; color: black; background: white; }
/* line 19, ../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.10.6/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
body ol, body ul { list-style: none; }
/* line 21, ../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.10.6/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
body table { border-collapse: separate; border-spacing: 0; vertical-align: middle; }
/* line 23, ../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.10.6/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
body caption, body th, body td { text-align: left; font-weight: normal; vertical-align: middle; }
/* line 25, ../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.10.6/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
body q, body blockquote { quotes: "" ""; }
/* line 96, ../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.10.6/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
body q:before, body q:after, body blockquote:before, body blockquote:after { content: ""; }
/* line 27, ../../../../../../../Library/Ruby/Gems/1.8/gems/compass-0.10.6/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
body a img { border: none; }
/* line 25, ../scss/_style.scss */
body * { font-size: 13px; }
/* line 29, ../scss/_style.scss */
body .ui-widget { font-size: 1em; }
/* line 33, ../scss/_style.scss */
strong { font-weight: bold; }
/* line 39, ../scss/_style.scss */
.cms-content-header { background-color: #afbfc7; background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #afbfc7), color-stop(100%, #91a7b2)); background-image: -moz-linear-gradient(top, #afbfc7 0%, #91a7b2 100%); background-image: linear-gradient(top, #afbfc7 0%, #91a7b2 100%); }
/* line 46, ../scss/_style.scss */
.cms-content-header h2 { float: left; padding: 10px; font-size: 14px; font-weight: bold; }
/* line 53, ../scss/_style.scss */
.cms-content-header .cms-content-header-tabs { float: left; }
/* line 60, ../scss/_style.scss */
.ui-tabs .cms-content-header .ui-tabs-nav li { height: 40px; }
/* line 63, ../scss/_style.scss */
.ui-tabs .cms-content-header .ui-tabs-nav li a { font-weight: bold; font-size: 11px; padding-top: 1em; }
/* line 72, ../scss/_style.scss */
.ui-tabs .cms-content-header .ui-state-default, .ui-tabs .cms-content-header .ui-widget-content .ui-state-default, .ui-tabs .cms-content-header .ui-widget-header .ui-state-default { background-color: #dddddd; background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #dddddd), color-stop(100%, #b7b7b7)); background-image: -moz-linear-gradient(top, #dddddd 0%, #b7b7b7 100%); background-image: linear-gradient(top, #dddddd 0%, #b7b7b7 100%); }
/* line 82, ../scss/_style.scss */
.ui-tabs .cms-content-header .ui-state-active, .ui-tabs .cms-content-header .ui-widget-content .ui-state-active, .ui-tabs .cms-content-header .ui-widget-header .ui-state-active { background: #ebeff1; }
/* line 87, ../scss/_style.scss */
.cms-content-tools { background-color: #dce3e6; }
/* line 93, ../scss/_style.scss */
.cms-header { padding: 10px; background-color: #00111d; background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #00111d), color-stop(50%, #003050), color-stop(100%, #00111d)); background-image: -moz-linear-gradient(top, #00111d 0%, #003050 50%, #00111d 100%); background-image: linear-gradient(top, #00111d 0%, #003050 50%, #00111d 100%); }
/* line 102, ../scss/_style.scss */
.cms-header * { color: white; }
/* line 109, ../scss/_style.scss */
.cms-logo .version { display: none; }
/* line 113, ../scss/_style.scss */
.cms-logo * { color: #3ebae0; }
/* line 117, ../scss/_style.scss */
.cms-logo a { display: inline-block; height: 25px; width: 25px; float: left; margin-right: 10px; background: url(../images/logo_small.png) no-repeat; text-indent: -9999em; }
/* line 130, ../scss/_style.scss */
.cms-login-status .logout-link { display: inline-block; height: 25px; width: 25px; float: left; margin-right: 10px; background: url(../images/logout.png) no-repeat; text-indent: -9999em; }
/* line 144, ../scss/_style.scss */
.ss-loading-screen, .ss-loading-screen .loading-logo { width: 100%; height: 100%; overflow: hidden; position: absolute; background: #fff; background: -moz-radial-gradient(50% 50% 180deg, circle cover, white, #efefef, #c7c7c7 100%); background: -webkit-gradient(radial, 50% 50%, 350, 50% 50%, 0, from(#e3e3e3), to(white)); z-index: 100000; margin: 0; padding: 0; }
/* line 159, ../scss/_style.scss */
.ss-loading-screen .loading-logo { background-url: url(../images/logo.gif); background-repeat: no-repeat; background-color: transparent; background-position: 50% 50%; }
/* line 165, ../scss/_style.scss */
.ss-loading-screen p { width: 100%; text-align: center; position: absolute; bottom: 80px; }
/* line 171, ../scss/_style.scss */
.ss-loading-screen p span.notice { display: inline-block; font-size: 14px; padding: 10px 20px; color: #dc7f00; border: none; -moz-border-radius: 5px; -webkit-border-radius: 5px; -o-border-radius: 5px; -ms-border-radius: 5px; -khtml-border-radius: 5px; border-radius: 5px; }
/* line 181, ../scss/_style.scss */
.ss-loading-screen .loading-animation { display: none; position: absolute; left: 49%; top: 75%; }
/** Messages (see sapphire/css/Form.css) */
/* line 200, ../scss/_style.scss */
.message { margin: 1em 0; padding: 0.5em; font-weight: bold; border: 1px black solid; }
/* line 206, ../scss/_style.scss */
.message.notice { background-color: #ffbe66; border-color: #ff9300; }
/* line 211, ../scss/_style.scss */
.message.warning { background-color: #ffbe66; border-color: #ff9300; }
/* line 215, ../scss/_style.scss */
.message.error { background-color: #ffbe66; border-color: #ff9300; }
/* line 224, ../scss/_style.scss */
.ModelAdmin .cms-content-tools { width: 300px; }
/* line 229, ../scss/_style.scss */
.ModelAdmin .ResultAssemblyBlock { display: none; }
/* line 236, ../scss/_style.scss */
.cms-page-add-form-dialog #PageType li { clear: left; height: 40px; border-bottom: 1px solid #333333; }
/* line 241, ../scss/_style.scss */
.cms-page-add-form-dialog #PageType li:hover, .cms-page-add-form-dialog #PageType li.selected { background-color: #ffff99; }
/* line 245, ../scss/_style.scss */
.cms-page-add-form-dialog #PageType li input, .cms-page-add-form-dialog #PageType li label, .cms-page-add-form-dialog #PageType li .icon, .cms-page-add-form-dialog #PageType li .title { float: left; }
/* line 249, ../scss/_style.scss */
.cms-page-add-form-dialog #PageType li .icon { width: 20px; }
/* line 253, ../scss/_style.scss */
.cms-page-add-form-dialog #PageType li .title { width: 100px; font-weight: bold; }
/* line 258, ../scss/_style.scss */
.cms-page-add-form-dialog #PageType li .description { font-style: italic; }
/* line 266, ../scss/_style.scss */
.cms-content-toolbar > * { display: inline-block; }
/* line 270, ../scss/_style.scss */
.cms-content-toolbar .cms-tree-view-modes * { display: inline-block; }
/* line 274, ../scss/_style.scss */
.cms-content-toolbar .cms-content-batchactions form * { display: inline-block; }
/* line 280, ../scss/_style.scss */
.cms-preview-header { background-color: #FFBE66; padding: 10px; font-weight: bold; }
/* line 287, ../scss/_style.scss */
.cms-preview { background-color: #b0bfc6; }
/* line 290, ../scss/_style.scss */
.cms-preview .cms-preview-toggle { cursor: pointer; }
/* line 293, ../scss/_style.scss */
.cms-preview .cms-preview-toggle a { color: white; font-weight: bold; text-decoration: none; }

BIN
admin/images/icons-32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
admin/images/logo_small.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 955 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

BIN
admin/images/logout.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 935 B

View File

@ -124,9 +124,7 @@ AssetTableField.prototype = {
}, },
deleteRecord: function(e) { deleteRecord: function(e) {
var img = Event.element(e); var self = this, img = Event.element(e), link = Event.findElement(e,"a"), row = Event.findElement(e,"tr");
var link = Event.findElement(e,"a");
var row = Event.findElement(e,"tr");
var linkCount = row.getElementsByClassName('linkCount')[0]; var linkCount = row.getElementsByClassName('linkCount')[0];
if(linkCount) linkCount = linkCount.innerHTML; if(linkCount) linkCount = linkCount.innerHTML;
@ -139,30 +137,23 @@ AssetTableField.prototype = {
if(confirmed) if(confirmed)
{ {
img.setAttribute("src",'sapphire/admin/images/network-save.gif'); // TODO doesn't work img.setAttribute("src",'sapphire/admin/images/network-save.gif'); // TODO doesn't work
new Ajax.Request( jQuery.ajax({
link.getAttribute("href"), url: link.getAttribute("href"),
{
method: 'post', method: 'post',
postBody: 'forceajax=1' + ($('SecurityID') ? '&SecurityID=' + $('SecurityID').value : ''), data: {forceajax: 1, SecurityID: jQuery('input[name=SecurityID]').val()},
onComplete: function(){ success: function() {
Effect.Fade( jQuery(row).fadeOut('fast', function() {
row,
{
afterFinish: function(obj) {
// remove row from DOM // remove row from DOM
obj.element.parentNode.removeChild(obj.element); this.element.parentNode.removeChild(obj.element);
// recalculate summary if needed (assumes that TableListField.js is present) // recalculate summary if needed (assumes that TableListField.js is present)
// TODO Proper inheritance // TODO Proper inheritance
if(this._summarise) this._summarise(); if(self._summarise) self._summarise();
// custom callback // custom callback
if(this.callback_deleteRecord) this.callback_deleteRecord(e); if(self.callback_deleteRecord) self.callback_deleteRecord(e);
}.bind(this)
} }
);
}.bind(this),
onFailure: this.ajaxErrorHandler
} }
); });
} }
Event.stop(e); Event.stop(e);

View File

@ -4,16 +4,16 @@
(function($) { (function($) {
$.entwine('ss', function($){ $.entwine('ss', function($){
/** /**
* Class: #Form_AddForm * Class: .add-form
* *
* Simple form with a page type dropdown * Simple form with a page type dropdown
* which creates a new page through #Form_EditForm and adds a new tree node. * which creates a new page through .cms-edit-form and adds a new tree node.
* *
* Requires: * Requires:
* ss.i18n * ss.i18n
* #Form_EditForm * .cms-edit-form
*/ */
$('#Form_AddForm').entwine({ $('.add-form').entwine({
/** /**
* Variable: Tree * Variable: Tree
* (DOMElement) * (DOMElement)
@ -40,7 +40,7 @@
Observable.applyTo(this[0]); Observable.applyTo(this[0]);
var tree = $('#sitetree_ul'); var tree = $('.cms-tree');
this.setTree(tree); this.setTree(tree);
// Event bindings // Event bindings
@ -91,7 +91,7 @@
data.push({name:button.attr('name'),value:button.val()}); data.push({name:button.attr('name'),value:button.val()});
// TODO Should be set by hiddenfield already // TODO Should be set by hiddenfield already
jQuery('#Form_EditForm').entwine('ss').loadForm( jQuery('.cms-edit-form').entwine('ss').loadForm(
this.attr('action'), this.attr('action'),
function() { function() {
// Tree updates are triggered by Form_EditForm load events // Tree updates are triggered by Form_EditForm load events

View File

@ -36,7 +36,7 @@
* Constructor: onmatch * Constructor: onmatch
*/ */
onmatch: function() { onmatch: function() {
var self = this, tree = $('#sitetree_ul'); var self = this, tree = $('.cms-tree');
this.setTree(tree); this.setTree(tree);
@ -44,16 +44,12 @@
self.serializeFromTree(); self.serializeFromTree();
}); });
// if tab which contains this form is shown, make the tree selectable $('.cms-tree-view-modes :input[name=view-mode]').bind('click', function(e) {
$('#TreeActions').bind('tabsselect', function(e, ui) { if($(e.target).val() == 'multiselect') {
// if we are selecting another tab, or the panel is visible (meaning about to be closed),
// disable tree selection and reset any values. Otherwise enable it.
if($(ui.panel).attr('id') != 'TreeActions-batchactions' || $(ui.panel).is(':visible')) {
// @TODO: this is unneccessarily fired also when switching between two other tabs
tree.removeClass('multiple');
} else {
tree.addClass('multiple'); tree.addClass('multiple');
self.serializeFromTree(); self.serializeFromTree();
} else {
tree.removeClass('multiple');
} }
}); });
@ -104,7 +100,7 @@
* (boolean) * (boolean)
*/ */
_isActive: function() { _isActive: function() {
return $('#TreeActions-batchactions').is(':visible'); return $('.cms-content-batchactions').is(':visible');
}, },
/** /**
@ -271,10 +267,10 @@
// // only if the current page was modified // // only if the current page was modified
// tree.jstree('select_node', selectedNode); // tree.jstree('select_node', selectedNode);
// } else if(data.deleted[selectedNodeId]) { // } else if(data.deleted[selectedNodeId]) {
// jQuery('#Form_EditForm').entwine('ss').removeForm(); // jQuery('.cms-edit-form').entwine('ss').removeForm();
// } // }
// } else { // } else {
// jQuery('#Form_EditForm').entwine('ss').removeForm(); // jQuery('.cms-edit-form').entwine('ss').removeForm();
// } // }
// close panel // close panel
@ -295,6 +291,11 @@
*/ */
$('#Form_BatchActionsForm select[name=Action]').entwine({ $('#Form_BatchActionsForm select[name=Action]').entwine({
onmatch: function() {
this.trigger('change');
this._super();
},
/** /**
* Function: onchange * Function: onchange
* *
@ -302,7 +303,13 @@
* (Event) e * (Event) e
*/ */
onchange: function(e) { onchange: function(e) {
$(e.target.form).entwine('ss').refreshSelected(); var form = $(e.target.form), btn = form.find(':submit');
if($(e.target).val() == -1) {
btn.attr('disabled', 'disabled');
} else {
btn.removeAttr('disabled');
form.entwine('ss').refreshSelected();
}
} }
}); });

View File

@ -5,7 +5,7 @@
$.entwine('ss', function($){ $.entwine('ss', function($){
/** /**
* Class: #Form_EditForm * Class: .cms-edit-form
* *
* Base edit form, provides ajaxified saving * Base edit form, provides ajaxified saving
* and reloading itself through the ajax return values. * and reloading itself through the ajax return values.
@ -19,7 +19,7 @@
* removeform - A form is about to be removed from the DOM * removeform - A form is about to be removed from the DOM
* load - Form is about to be loaded through ajax * load - Form is about to be loaded through ajax
*/ */
$('#Form_EditForm').entwine(/** @lends ss.Form_EditForm */{ $('.cms-edit-form').entwine(/** @lends ss.Form_EditForm */{
/** /**
* Variable: PlaceholderHtml * Variable: PlaceholderHtml
* (String_ HTML text to show when no form content is chosen. * (String_ HTML text to show when no form content is chosen.
@ -41,6 +41,27 @@
this._setupChangeTracker(); this._setupChangeTracker();
$('.cms-tree').bind('select_node.jstree', function(e, data) {
var node = data.rslt.obj, loadedNodeID = self.find(':input[name=ID]').val()
// Don't allow checking disabled nodes
if($(node).hasClass('disabled')) return false;
// Don't allow reloading of currently selected node,
// mainly to avoid doing an ajax request on initial page load
if($(node).data('id') == loadedNodeID) return;
var url = $(node).find('a:first').attr('href');
if(url && url != '#') {
var xmlhttp = self.loadForm(
url,
function(response) {}
);
} else {
self.removeForm();
}
});
// Can't bind this through jQuery // Can't bind this through jQuery
window.onbeforeunload = function(e) {return self._checkChangeTracker(false);}; window.onbeforeunload = function(e) {return self._checkChangeTracker(false);};
@ -163,6 +184,9 @@
self._loadResponse(xmlhttp.responseText, status, xmlhttp, formData); self._loadResponse(xmlhttp.responseText, status, xmlhttp, formData);
} }
// Re-init tabs (in case the form tag itself is a tabset)
if(self.hasClass('ss-tabset')) self.removeClass('ss-tabset').addClass('ss-tabset');
// re-select previously saved tabs // re-select previously saved tabs
$.each(selectedTabs, function(i, selectedTab) { $.each(selectedTabs, function(i, selectedTab) {
self.find('#' + selectedTab.id).tabs('select', selectedTab.selected); self.find('#' + selectedTab.id).tabs('select', selectedTab.selected);
@ -308,8 +332,8 @@
this.removeForm(); this.removeForm();
} }
// @todo Coupling to avoid FOUC (entwine applies to late) // If the form itself is a tabset, force re-rendering
this.find('.ss-tabset').tabs(); if(this.hasClass('ss-tabset')) this.tabs('destroy').tabs();
this._setupChangeTracker(); this._setupChangeTracker();
@ -327,7 +351,7 @@
// focus input on first form element // focus input on first form element
this.find(':input:visible:first').focus(); this.find(':input:visible:first').focus();
this.trigger('loadnewpage', {data: data, origData: origData}); this.trigger('loadnewpage', {data: data, origData: origData, xmlhttp: xmlhttp});
} }
// set status message based on response // set status message based on response
@ -340,29 +364,29 @@
}); });
/** /**
* Class: #Form_EditForm .Actions :submit * Class: .cms-edit-form .Actions :submit
* *
* All buttons in the right CMS form go through here by default. * All buttons in the right CMS form go through here by default.
* We need this onclick overloading because we can't get to the * We need this onclick overloading because we can't get to the
* clicked button from a form.onsubmit event. * clicked button from a form.onsubmit event.
*/ */
$('#Form_EditForm .Actions :submit').entwine({ $('.cms-edit-form .Actions :submit').entwine({
/** /**
* Function: onclick * Function: onclick
*/ */
onclick: function(e) { onclick: function(e) {
jQuery('#Form_EditForm').entwine('ss').ajaxSubmit(this); jQuery('.cms-edit-form').entwine('ss').ajaxSubmit(this);
return false; return false;
} }
}); });
/** /**
* Class: #Form_EditForm textarea.htmleditor * Class: .cms-edit-form textarea.htmleditor
* *
* Add tinymce to HtmlEditorFields within the CMS. * Add tinymce to HtmlEditorFields within the CMS.
*/ */
$('#Form_EditForm textarea.htmleditor').entwine({ $('.cms-edit-form textarea.htmleditor').entwine({
/** /**
* Constructor: onmatch * Constructor: onmatch

View File

@ -0,0 +1,102 @@
(function($) {
$.entwine('ss', function($){
$('.LeftAndMain .cms-preview').entwine({
onmatch: function() {
var self = this, layoutContainer = this.parent();
// this.resizable({
// handles: 'w',
// stop: function(e, ui) {
// $('.cms-container').layout({resize: false});
// }
// });
// Create layout and controls
this.prepend('<div class="cms-preview-toggle west"><a href="#">&lt;</a></div>');
this.find('iframe').addClass('center');
this.layout({type: 'border'});
this.find('iframe').bind('load', function() {self._fixIframeLinks();});
self._fixIframeLinks();
$('.cms-edit-form').bind('loadnewpage', function(e, ui) {
// var url = ui.xmlhttp.getResponseHeader('x-frontend-url');
var url = $(this).find(':input[name=StageURLSegment]').val();
if(url) self.loadUrl(url);
});
this._super();
},
loadUrl: function(url) {
this.find('iframe').attr('src', url);
},
_fixIframeLinks: function() {
var doc = this.find('iframe')[0].contentDocument;
// Block outside links from going anywhere
var links = doc.getElementsByTagName('A');
for (var i = 0; i < links.length; i++) {
var href = links[i].getAttribute('href');
if (href && href.match(/^http:\/\//)) {
links[i].setAttribute('href', 'javascript:false');
}
}
// Load this page in the admin interface if appropriate
var id = $(doc).find('meta[name=x-page-id]').attr('content'), form = $('.cms-edit-form');
// TODO Remove hardcoding
if(id && form.find(':input[name=ID]').val() != id) form.loadForm('admin/page/edit/show/' + id);
},
toggle: function() {
var self = this,
width = this.width(),
relayout = function() {$('.cms-container').layout({resize: false});},
minWidth = this.find('.cms-preview-toggle').width(),
wasCollapsed = (width <= minWidth),
sharedWidth = $('.cms-content').width() / 2, // half of content area by default
newWidth = wasCollapsed ? sharedWidth : minWidth,
newOverflow = wasCollapsed ? 'auto' : 'hidden';
this.css('overflow', newOverflow).width(newWidth);
this.toggleClass('collapsed', !wasCollapsed).toggleClass('expanded', wasCollapsed);
this.find('iframe').toggle(wasCollapsed);
relayout();
// this.css('overflow', newOverflow).animate(
// {width: newWidth+'px'},
// {
// duration: 500,
// complete: function() {
// relayout();
// self.toggleClass('collapsed', !wasCollapsed).toggleClass('expanded', wasCollapsed);
// self.find('iframe').toggle(wasCollapsed);
// },
// step: relayout
// }
// );
}
});
$('.LeftAndMain .cms-preview.collapsed').entwine({
onmatch: function() {
this.find('a').text('<');
}
});
$('.LeftAndMain .cms-preview.expanded').entwine({
onmatch: function() {
this.find('a').text('>');
}
});
$('.LeftAndMain .cms-preview .cms-preview-toggle').entwine({
onclick: function(e) {
e.preventDefault();
this.parents('.cms-preview').toggle();
}
});
});
}(jQuery));

View File

@ -6,7 +6,7 @@
$.entwine('ss', function($){ $.entwine('ss', function($){
$('#sitetree_ul').entwine({ $('.cms-tree').entwine({
onmatch: function() { onmatch: function() {
this._super(); this._super();
@ -79,9 +79,11 @@
"drag_target" : false "drag_target" : false
}, },
'themes': { 'themes': {
'theme': 'apple' 'theme': 'apple',
'url': 'sapphire/thirdparty/jstree/themes/apple/style.css'
}, },
// 'plugins': ['html_data', 'ui', 'dnd', 'crrm', 'themeroller'] // Caution: SilverStripe has disabled $.vakata.css.add_sheet() for performance reasons,
// which means you need to add any CSS manually to sapphire/admin/scss/_tree.css
'plugins': [ 'plugins': [
'html_data', 'ui', 'dnd', 'crrm', 'themes', 'html_data', 'ui', 'dnd', 'crrm', 'themes',
'checkbox' // checkboxes are hidden unless .multiple is set 'checkbox' // checkboxes are hidden unless .multiple is set
@ -107,8 +109,8 @@
}) })
.bind('before.jstree', function(e, data) { .bind('before.jstree', function(e, data) {
if(data.func == 'start_drag') { if(data.func == 'start_drag') {
// Only allow drag'n'drop if it has been specifically enabled, or the tree is in search mode // Don't allow drag'n'drop if multi-select is enabled'
if(!$('input[id=sortitems]').is(':checked') || self.data('searchparams')) { if(!self.hasClass('draggable') || self.hasClass('multiselect')) {
e.stopImmediatePropagation(); e.stopImmediatePropagation();
return false; return false;
} }
@ -122,27 +124,6 @@
} }
} }
}) })
// TODO Move to EditForm logic
.bind('select_node.jstree', function(e, data) {
var node = data.rslt.obj, loadedNodeID = $('#Form_EditForm :input[name=ID]').val()
// Don't allow checking disabled nodes
if($(node).hasClass('disabled')) return false;
// Don't allow reloading of currently selected node,
// mainly to avoid doing an ajax request on initial page load
if($(node).data('id') == loadedNodeID) return;
var url = $(node).find('a:first').attr('href');
if(url && url != '#') {
var xmlhttp = $('#Form_EditForm').loadForm(
url,
function(response) {}
);
} else {
$('#Form_EditForm').removeForm();
}
})
.bind('move_node.jstree', function(e, data) { .bind('move_node.jstree', function(e, data) {
var movedNode = data.rslt.o, newParentNode = data.rslt.np, oldParentNode = data.inst._get_parent(movedNode); var movedNode = data.rslt.o, newParentNode = data.rslt.np, oldParentNode = data.inst._get_parent(movedNode);
var siblingIDs = $.map($(movedNode).siblings().andSelf(), function(el) { var siblingIDs = $.map($(movedNode).siblings().andSelf(), function(el) {
@ -159,7 +140,7 @@
}); });
}); });
$('#Form_EditForm').bind('loadnewpage', function(e, data) { $('.cms-edit-form').bind('loadnewpage', function(e, data) {
self._onLoadNewPage(e, data); self._onLoadNewPage(e, data);
}); });
}, },
@ -262,7 +243,7 @@
}); });
}); });
$('#sitetree_ul.multiple').entwine({ $('.cms-tree.multiple').entwine({
onmatch: function() { onmatch: function() {
this._super(); this._super();
@ -284,7 +265,7 @@
} }
}); });
$('#sitetree_ul li').entwine({ $('.cms-tree li').entwine({
/** /**
* Function: setEnabled * Function: setEnabled
@ -317,4 +298,15 @@
} }
}); });
$('.cms-tree-view-modes :input[name=view-mode]').entwine({
onmatch: function() {
// set active by default
this.trigger('click');
this._super();
},
onclick: function(e) {
$('.cms-tree').toggleClass('draggable', $(e.target).val() == 'draggable');
}
});
}(jQuery)); }(jQuery));

View File

@ -1,15 +1,12 @@
/** /**
* File: LeftAndMain.js * File: LeftAndMain.js
*/ */
/**
* Variable: ss_MainLayout
* jquery.layout Global variable so layout state management can pick it up.
*/
var ss_MainLayout;
(function($) { (function($) {
$.metadata.setType('html5');
$.entwine('ss', function($){ $.entwine('ss', function($){
/** /**
* Position the loading spinner animation below the ss logo * Position the loading spinner animation below the ss logo
*/ */
@ -44,11 +41,6 @@ var ss_MainLayout;
* loadnewpage - ... * loadnewpage - ...
*/ */
$('.LeftAndMain').entwine({ $('.LeftAndMain').entwine({
/**
* Variable: MainLayout
* (Object) Reference to jQuery.layout element
*/
MainLayout: null,
/** /**
* Variable: PingIntervalSeconds * Variable: PingIntervalSeconds
@ -62,114 +54,39 @@ var ss_MainLayout;
onmatch: function() { onmatch: function() {
var self = this; var self = this;
// Browser detection
if($.browser.msie && parseInt($.browser.version, 10) < 7) {
$('.ss-loading-screen').append(
'<p><span class="notice">' +
ss.i18n._t('LeftAndMain.IncompatBrowserWarning') +
'</span></p>'
);
return;
}
// Initialize layouts, inner to outer
var doInnerLayout = function() {$('.cms-content').layout();}
var outer = $('.cms-container');
var doOuterLayout = function() {outer.layout({resize: false});}
doInnerLayout();
doOuterLayout();
$(window).resize(doOuterLayout);
// Remove loading screen // Remove loading screen
$('.ss-loading-screen').hide(); $('.ss-loading-screen').hide();
$('body').removeClass('stillLoading'); $('body').removeClass('loading');
$(window).unbind('resize', positionLoadingSpinner); $(window).unbind('resize', positionLoadingSpinner);
// Layout
ss_MainLayout = this._setupLayout();
this.setMainLayout(ss_MainLayout);
layoutState.options.keys = "west.size,west.isClosed";
$(window).unload(function(){ layoutState.save('ss_MainLayout');});
this._setupPinging(); this._setupPinging();
// HACK Delay resizing to give jquery-ui tabs a change their dimensions $('.cms-edit-form').live('loadnewpage', function() {
// through dynamically added css classes doInnerLayout();
$(window).resize(function () { doOuterLayout();
var timerID = "timerLeftAndMainResize";
if (window[timerID]) clearTimeout(window[timerID]);
window[timerID] = setTimeout(function() {
self._resizeChildren();
}, 200);
});
$(window).resize();
// If tab has no nested tabs, set overflow to auto
$(this).find('.tab').not(':has(.tab)').css('overflow', 'auto');
// @todo Doesn't resize properly if the response doesn't contain a tabset (see above)
$('#Form_EditForm').bind('loadnewpage', function() {
// HACK Delay resizing to give jquery-ui tabs a change their dimensions
// through dynamically added css classes
var timerID = "timerLeftAndMainResize";
if (window[timerID]) clearTimeout(window[timerID]);
window[timerID] = setTimeout(function() {
self._resizeChildren();
}, 200);
}); });
this._super(); this._super();
}, },
/**
* Function: _setupLayout
*
* Initialize jQuery layout manager with the following panes:
* - east: Tree, Page Version History, Site Reports
* - center: Form
* - west: "Insert Image", "Insert Link", "Insert Flash" panes
* - north: CMS area menu bar
* - south: "Page view", "profile" and "logout" links
*/
_setupLayout: function() {
var self = this;
var widthEast = this.find('.ui-layout-east').width();
var widthWest = this.find('.ui-layout-west').width();
// layout containing the tree, CMS menu, the main form etc.
var savedLayoutSettings = layoutState.load('ss_MainLayout');
var layoutSettings = jQuery.extend({
defaults: {
// TODO Reactivate once we have localized values
togglerTip_open: '',
togglerTip_closed: '',
resizerTip: '',
sliderTip: '',
onresize: function() {self._resizeChildren();},
onopen: function() {self._resizeChildren();}
},
north: {
slidable: false,
resizable: false,
size: this.find('.ui-layout-north').height(),
togglerLength_open: 0
},
south: {
slidable: false,
resizable: false,
size: this.find('.ui-layout-south').height(),
togglerLength_open: 0
},
west: {
size: (widthWest) ? widthWest : undefined,
fxName: "none"
},
east: {
initClosed: true,
// multiple panels which are triggered through tinymce buttons,
// so a user shouldn't be able to toggle this panel manually
initHidden: true,
spacing_closed: 0,
fxName: "none"
},
center: {}
}, savedLayoutSettings);
var layout = $('body').layout(layoutSettings);
// Adjust tree accordion etc. in left panel to work correctly
// with jQuery.layout (see http://layout.jquery-dev.net/tips.html#Widget_Accordion)
this.find("#treepanes").accordion({
fillSpace: true,
animated: false
});
return layout;
},
/** /**
* Function: _setupPinging * Function: _setupPinging
* *
@ -197,31 +114,6 @@ var ss_MainLayout;
complete: onSessionLost complete: onSessionLost
}); });
}, this.getPingIntervalSeconds() * 1000); }, this.getPingIntervalSeconds() * 1000);
},
/**
* Function: _resizeChildren
*
* Resize elements in center panel
* to fit the boundary box provided by the layout manager.
*
* Todo:
* Replace with automated less ugly parent/sibling traversal
*/
_resizeChildren: function() {
$("#treepanes", this).accordion("resize");
$('#sitetree_and_tools', this).fitHeightToParent();
$('#contentPanel form', this).fitHeightToParent();
$('#contentPanel form fieldset', this).fitHeightToParent();
$('#contentPanel form fieldset .content', this).fitHeightToParent();
$('#Form_EditForm').fitHeightToParent();
$('#Form_EditForm fieldset', this).fitHeightToParent();
// Order of resizing is important: Outer to inner
// TODO Only supports two levels of tabs at the moment
$('#Form_EditForm fieldset > .ss-tabset', this).fitHeightToParent();
$('#Form_EditForm fieldset > .ss-tabset > .tab', this).fitHeightToParent();
$('#Form_EditForm fieldset > .ss-tabset > .tab > .ss-tabset', this).fitHeightToParent();
$('#Form_EditForm fieldset > .ss-tabset > .tab > .ss-tabset > .tab', this).fitHeightToParent();
} }
}); });
@ -233,63 +125,15 @@ var ss_MainLayout;
* a new 'clickedButton' property on the form DOM element. * a new 'clickedButton' property on the form DOM element.
*/ */
$('.LeftAndMain :submit, .LeftAndMain button, .LeftAndMain :reset').entwine({ $('.LeftAndMain :submit, .LeftAndMain button, .LeftAndMain :reset').entwine({
/**
* Constructor: onmatch
*/
onmatch: function() { onmatch: function() {
this.addClass( // TODO Adding classes in onmatch confuses entwine
'ui-state-default ' + var self = this;
'ui-corner-all' setTimeout(function() {self.addClass('ss-ui-button');}, 10);
)
.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');
})
.click(function() {
var form = this.form;
// forms don't natively store the button they've been triggered with
form.clickedButton = this;
// Reset the clicked button shortly after the onsubmit handlers
// have fired on the form
setTimeout(function() {form.clickedButton = null;}, 10);
});
this._super(); this._super();
} }
}); });
/**
* Class: #TreeActions
*
* Container for tree actions like "create", "search", etc.
*/
$('#TreeActions').entwine({
/**
* Constructor: onmatch
*
* Setup "create", "search", "batch actions" layers above tree.
* All tab contents are closed by default.
*/
onmatch: function() {
this.tabs({
collapsible: true,
selected: parseInt(jQuery.cookie('ui-tabs-TreeActions'), 10) || null,
cookie: { expires: 30, path: '/', name: 'ui-tabs-TreeActions' }
});
}
});
/** /**
* Class: a#EditMemberProfile * Class: a#EditMemberProfile
* *
@ -402,7 +246,7 @@ var ss_MainLayout;
onmatch: function() { onmatch: function() {
this._super(); this._super();
$('#Form_EditForm').bind('loadnewpage delete', function(e) { $('.cms-edit-form').bind('loadnewpage delete', function(e) {
var updatedSwitchView = $('#AjaxSwitchView'); var updatedSwitchView = $('#AjaxSwitchView');
if(updatedSwitchView.length) { if(updatedSwitchView.length) {
$('#SwitchView').html(updatedSwitchView.html()); $('#SwitchView').html(updatedSwitchView.html());
@ -431,6 +275,7 @@ var ss_MainLayout;
return false; return false;
} }
}); });
}); });
}(jQuery)); }(jQuery));

View File

@ -88,24 +88,24 @@ AjaxMemberLookup = {
* Class: MemberTableField * Class: MemberTableField
*/ */
MemberTableField = Class.create(); MemberTableField = Class.create();
MemberTableField.applyTo('#Form_EditForm div.MemberTableField'); MemberTableField.applyTo('.cms-edit-form div.MemberTableField');
MemberTableField.prototype = { MemberTableField.prototype = {
initialize: function() { initialize: function() {
Behaviour.register({ Behaviour.register({
'#Form_EditForm div.MemberFilter input' : { '.cms-edit-form div.MemberFilter input' : {
onkeypress : this.prepareSearch.bind(this) onkeypress : this.prepareSearch.bind(this)
}, },
'#Form_EditForm div.MemberTableField table.data tr.addtogrouprow input' : { '.cms-edit-form div.MemberTableField table.data tr.addtogrouprow input' : {
onkeypress : this.prepareAddToGroup.bind(this) onkeypress : this.prepareAddToGroup.bind(this)
}, },
'#Form_EditForm div.MemberTableField table.data tr.addtogrouprow #Form_AddRecordForm_action_addtogroup' : { '.cms-edit-form div.MemberTableField table.data tr.addtogrouprow #Form_AddRecordForm_action_addtogroup' : {
onclick : this.prepareAddToGroup.bind(this) onclick : this.prepareAddToGroup.bind(this)
}, },
'#Form_EditForm div.MemberTableField table.data tr.addtogrouprow td.actions input' : { '.cms-edit-form div.MemberTableField table.data tr.addtogrouprow td.actions input' : {
initialise: function() { initialise: function() {
data = this.parentNode.parentNode.getElementsByTagName('input'); data = this.parentNode.parentNode.getElementsByTagName('input');
var i,item,error = []; var i,item,error = [];
@ -116,9 +116,9 @@ MemberTableField.prototype = {
onclick : this.addToGroup.bind(this) onclick : this.addToGroup.bind(this)
}, },
//'#Form_EditForm div.MemberTableField input' : AjaxMemberLookup, //'.cms-edit-form div.MemberTableField input' : AjaxMemberLookup,
'#Form_EditForm' : { '.cms-edit-form' : {
changeDetection_fieldsToIgnore : { changeDetection_fieldsToIgnore : {
'ctf[start]' : true, 'ctf[start]' : true,
'ctf[ID]' : true, 'ctf[ID]' : true,
@ -307,14 +307,7 @@ MemberFilterButton.prototype = {
} }
updateURL += ($('SecurityID') ? '&SecurityID=' + $('SecurityID').value : ''); updateURL += ($('SecurityID') ? '&SecurityID=' + $('SecurityID').value : '');
new Ajax.Updater( fieldID, updateURL, { jQuery($(fieldID)).get(updateURL, null, function() {Behaviour.apply($(fieldID), true);});
onComplete: function() {
Behaviour.apply($(fieldID), true);
},
onFailure: function( response ) {
errorMessage('Could not filter results: ' + response.responseText );
}
});
} catch(er) { } catch(er) {
errorMessage('Error searching'); errorMessage('Error searching');
} }
@ -325,7 +318,7 @@ MemberFilterButton.prototype = {
// has to be external from initialize() because otherwise request will double on each reload - WTF // has to be external from initialize() because otherwise request will double on each reload - WTF
Behaviour.register({ Behaviour.register({
'#Form_EditForm div.MemberTableField table.data input.text' : AjaxMemberLookup '.cms-edit-form div.MemberTableField table.data input.text' : AjaxMemberLookup
}); });
/** /**
@ -346,12 +339,10 @@ function ajaxSubmitFieldSet(href, fieldSet, extraData) {
'method' : 'post', 'method' : 'post',
'data' : data, 'data' : data,
'success' : function(response) { 'success' : function(response) {
//alert(response.responseText); eval(response);
Ajax.Evaluator(response);
}, },
'error' : function(response) { 'error' : function(response) {
alert(response.responseText); alert(response.responseText);
//errorMessage('Error: ', response);
} }
}); });
} }

View File

@ -116,7 +116,7 @@
this.trigger('historyGoBack', {url:previousPage}); this.trigger('historyGoBack', {url:previousPage});
// load new location // load new location
$('#Form_EditForm').loadForm(previousPage); $('.cms-edit-form').loadForm(previousPage);
this.redraw(); this.redraw();
} }
@ -136,7 +136,7 @@
this.trigger('historyGoForward', {url:nextPage}); this.trigger('historyGoForward', {url:nextPage});
// load new location // load new location
$('#Form_EditForm').loadForm(nextPage); $('.cms-edit-form').loadForm(nextPage);
this.redraw(); this.redraw();
} }

View File

@ -64,14 +64,14 @@
// Import forms are processed without ajax // Import forms are processed without ajax
if(this.attr('id').match(/^Form_ImportForm/)) return true; if(this.attr('id').match(/^Form_ImportForm/)) return true;
$('#contentPanel').closeRightPanel(); $('.cms-editor-dialogs').closeRightPanel();
this.trigger('beforeSubmit'); this.trigger('beforeSubmit');
var btn = $(this[0].clickedButton); var btn = $(this[0].clickedButton);
btn.addClass('loading'); btn.addClass('loading');
$('#Form_EditForm').loadForm( $('.cms-edit-form').loadForm(
this.attr('action'), this.attr('action'),
function() { function() {
btn.removeClass('loading'); btn.removeClass('loading');
@ -135,7 +135,7 @@
onclick: function(e) { onclick: function(e) {
var firstLink = this.find('a[href]'); var firstLink = this.find('a[href]');
if(!firstLink) return; if(!firstLink) return;
$('#Form_EditForm').loadForm(firstLink.attr('href')); $('.cms-edit-form').loadForm(firstLink.attr('href'));
return false; return false;
} }
}); });
@ -153,7 +153,7 @@
className = $('select option:selected', this).val(); className = $('select option:selected', this).val();
requestPath = this.attr('action').replace('ManagedModelsSelect', className + '/add'); requestPath = this.attr('action').replace('ManagedModelsSelect', className + '/add');
var $button = $(':submit', this); var $button = $(':submit', this);
$('#Form_EditForm').loadForm( $('.cms-edit-form').loadForm(
requestPath, requestPath,
function() { function() {
$button.removeClass('loading'); $button.removeClass('loading');
@ -166,11 +166,11 @@
}); });
/** /**
* Class: #Form_EditForm input[name=action_doDelete] * Class: .cms-edit-form input[name=action_doDelete]
* *
* RHS panel Delete button * RHS panel Delete button
*/ */
$('#Form_EditForm input[name=action_doDelete]').entwine({ $('.cms-edit-form input[name=action_doDelete]').entwine({
// Function: onclick // Function: onclick
onclick: function(e) { onclick: function(e) {
if(!confirm(ss.i18n._t('ModelAdmin.REALLYDELETE', 'Really delete?'))) { if(!confirm(ss.i18n._t('ModelAdmin.REALLYDELETE', 'Really delete?'))) {
@ -197,14 +197,14 @@
} }
}); });
$('#contentPanel').entwine({ $('.cms-editor-dialogs').entwine({
/** /**
* Close TinyMCE image, link or flash panel. * Close TinyMCE image, link or flash panel.
* this function is called everytime a new search, back or add new DataObject are clicked * this function is called everytime a new search, back or add new DataObject are clicked
**/ **/
closeRightPanel: function(){ closeRightPanel: function(){
if($('#contentPanel').is(':visible')) { if($('.cms-editor-dialogs').is(':visible')) {
$('#contentPanel').hide(); $('.cms-editor-dialogs').hide();
$('#Form_EditorToolbarImageForm').hide(); $('#Form_EditorToolbarImageForm').hide();
$('#Form_EditorToolbarFlashForm').hide(); $('#Form_EditorToolbarFlashForm').hide();
$('#Form_EditorToolbarLinkForm').hide(); $('#Form_EditorToolbarLinkForm').hide();

View File

@ -1,12 +0,0 @@
/**
* File: SecurityAdmin.Tree.js
*
* Configuration for the left hand tree
*/
if(typeof SiteTreeHandlers == 'undefined') SiteTreeHandlers = {};
SiteTreeHandlers.parentChanged_url = 'admin/security/ajaxupdateparent';
SiteTreeHandlers.orderChanged_url = 'admin/security/ajaxupdatesort';
SiteTreeHandlers.loadPage_url = 'admin/security/getitem';
SiteTreeHandlers.loadTree_url = 'admin/security/getsubtree';
SiteTreeHandlers.showRecord_url = 'admin/security/show/';
SiteTreeHandlers.controller_url = 'admin/security';

View File

@ -12,7 +12,7 @@
if(memberTableField) memberTableField.refresh(); if(memberTableField) memberTableField.refresh();
// Refresh tree // Refresh tree
var tree = $(window.parent.document).find('#sitetree').get(0); var tree = $(window.parent.document).find('.cms-tree').get(0);
if(tree) tree.reload(); if(tree) tree.reload();
} }
}; };
@ -53,9 +53,9 @@
$.entwine('ss', function($){ $.entwine('ss', function($){
/** /**
* Class: #Form_EditForm .Actions #Form_EditForm_action_addmember * Class: .cms-edit-form .Actions #Form_EditForm_action_addmember
*/ */
$('#Form_EditForm .Actions #Form_EditForm_action_addmember').entwine({ $('.cms-edit-form .Actions #Form_EditForm_action_addmember').entwine({
// Function: onclick // Function: onclick
onclick: function(e) { onclick: function(e) {
// CAUTION: Assumes that a MemberTableField-instance is present as an editing form // CAUTION: Assumes that a MemberTableField-instance is present as an editing form

View File

@ -1,4 +1,66 @@
(function($) { (function($) {
$('.ss-ui-button').entwine({
/**
* Constructor: onmatch
*/
onmatch: function() {
this.addClass(
'ui-state-default ' +
'ui-corner-all'
)
.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');
})
.click(function() {
var form = this.form;
// forms don't natively store the button they've been triggered with
form.clickedButton = this;
// Reset the clicked button shortly after the onsubmit handlers
// have fired on the form
setTimeout(function() {form.clickedButton = null;}, 10);
});
this._super();
}
});
/**
* Creates a jQuery UI tab navigation bar, detached from the container DOM structure.
*/
$('.ss-ui-tabs-nav').entwine({
onmatch: function() {
this.addClass('ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-panel ui-corner-bottom');
this.find('ul').addClass('ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all');
this.find('li').addClass('ui-state-default ui-corner-top');
// TODO Figure out selected tab
this.find('li:first').selectIt();
this._super();
}
});
$('.ss-ui-tabs-nav li').entwine({
onclick: function() {
this.selectIt();
},
selectIt: function() {
var cls = 'ui-tabs-selected ui-state-active';
this.addClass(cls).siblings().not(this).removeClass(cls);
}
});
$.widget("ssui.titlebar", { $.widget("ssui.titlebar", {
_create: function() { _create: function() {
this.originalTitle = this.element.attr('title'); this.originalTitle = this.element.attr('title');

43
admin/scss/_colours.scss Normal file
View File

@ -0,0 +1,43 @@
/**
* This file only contains color definitions.
* Please put any further formatting into _style.scss.
*/
// colour definitions
$color-base: #B0BFC6;
$color-widget-bg: lighten($color-base, 20%);
$color-dark-bg: #003050;
$color-shadow-light: #aaa;
$color-shadow-dark: #333;
$color-highlight: #FFFF66;
$color-menu-button: #338DC1; // darkened and desaturated as required
$color-text: #444;
$color-text-light: white;
$color-text-light-link: white;
$color-text-dark: #333;
$color-text-dark-link: #3EBAE0;
$color-button-constructive: #77b53f;
$color-button-destructive: #f00;
$color-warning: #FF9300;
$color-error: #FF9300;
$color-notice: #FF9300;
/*
// simple grayscale theme
$color-base: grayscale(#B0BFC6);
$color-widget-bg: grayscale(lighten($color-base, 20%));
$color-dark-bg: grayscale(#003050);
$color-shadow-light: grayscale(#aaa);
$color-shadow-dark: grayscale(#333);
$color-menu-button: grayscale(#338DC1);
$color-text: grayscale(#444);
$color-text-light: white;
$color-text-light-link: white;
$color-text-dark: grayscale(#333);
$color-text-dark-link: grayscale(#3EBAE0);
$color-button-constructive: grayscale(#77b53f);
$color-button-destructive: grayscale(#f00);
$color-warning: grayscale(#FF9300);
$color-error: grayscale(#FF9300);
$color-notice: grayscale(#FF9300);
*/

125
admin/scss/_forms.scss Normal file
View File

@ -0,0 +1,125 @@
/**
* This file defines common styles for form elements used throughout the CMS interface.
* It is an addition to the base styles defined in sapphire/css/Form.css.
*/
.cms {
// Form fields
.field {
display: block;
padding: 10px 0;
border-bottom: 1px solid $color-shadow-light;
label {
float: left;
width: 10em;
}
// Don't float inner/contained fields
.middleColumn {
margin-left: 10em;
.field {
display: inline;
padding: 0;
border: none;
}
label {
float: none;
width: auto;
}
}
}
// TODO Change to mixin?
form.nostyle {
.field {
display: inline;
padding: 0;
border: 0;
}
label {
float: none;
width: auto;
}
.middleColumn {
margin-left: 0;
}
}
.field.nolabel {
.middleColumn {
margin-left: 0;
}
}
input, textarea {
@include border-radius(5px);
@include linear-gradient(color-stops(
$color-widget-bg,
lighten($color-widget-bg, 10%),
$color-widget-bg
));
border: 1px solid $color-shadow-light;
padding: 3px;
}
input.loading {
padding-left: 16px;
background: $color-widget-bg url(../../images/network-save.gif) no-repeat center left;
}
// Buttons
.ss-ui-button.ss-ui-action-constructive,
.ui-widget-content .ss-ui-button.ss-ui-action-constructive,
.ui-widget-header .ss-ui-button.ss-ui-action-constructive {
background: none; // avoid overwritten gradient from jQuery UI styles
@include linear-gradient(color-stops(
$color-button-constructive,
darken($color-button-constructive, 20%)
));
color: $color-text-light;
}
.ss-ui-button.ss-ui-action-destructive,
.ui-widget-content .ss-ui-button.ss-ui-action-destructive,
.ui-widget-header .ss-ui-button.ss-ui-action-destructive {
color: $color-button-destructive;
}
.ss-ui-button.ss-ui-action-minor,
.ui-widget-content .ss-ui-button.ss-ui-action-minor
.ui-widget-header .ss-ui-button.ss-ui-action-minor {
background: none;
padding: 0;
border: 0;
color: $color-text-dark;
text-decoration: underline;
}
.cms-edit-form {
padding-bottom: 20px;
// TODO Unclear if "button bar" concept is edit form specific
.Actions {
text-align: right;
}
}
.cms-content-tools {
.field {
label {
float: none;
width: auto;
}
.middleColumn {
margin-left: 0;
}
}
}
}

87
admin/scss/_layout.scss Normal file
View File

@ -0,0 +1,87 @@
/**
* This file defines the structural layout of the CMS interface.
* Ideally, you should be able to lay out the base elements of the CMS with only this file.
*
* Please put any presentational definitions (color, fonts, backgrounds, paddings)
* into _style.scss or _uitheme.scss instead.
*/
html, body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
overflow: hidden; // avoid jlayout jitter when resizing
}
.cms-preview {
width: 1px; // collapsed by default
overflow: hidden;
.cms-preview-toggle {
width: 10px;
}
iframe {
width: 100%;
height: 100%;
}
}
.cms-container {
height: 100%
}
.cms-menu {
width: 250px;
overflow: auto;
}
.cms-content {
}
.cms-menu,
.cms-content,
.cms-content-header,
.cms-content-tools,
.cms-content-form {
@include inline-block;
}
.cms-content-tools {
width: 250px;
overflow: auto;
}
.cms-content-form {
overflow: auto;
}
.cms-content-header {
height: 40px;
h2 {
width: 250px - (10px*2) /* padding */ - 4px /* em adjustment hacketyhack */;
}
& > div {
width: 9999em;
overflow: hidden;
}
}
.cms-content-actions {
padding: 10px;
}
.cms-logo {
height: 30px;
overflow: hidden;
vertical-align: middle;
}
.cms-login-status {
height: 30px;
overflow: hidden;
vertical-align: middle;
}

92
admin/scss/_menu.scss Normal file
View File

@ -0,0 +1,92 @@
.cms-menu {
z-index: 10;
background-color: $color-widget-bg;
@include box-shadow($color-shadow-light, 3px, 0, 3px);
a {
text-decoration: none;
}
}
.cms-menu-list {
li {
background-color: $color-base;
@include linear-gradient(color-stops(
$color-base,
darken($color-base, 20%)
));
border-bottom: 1px solid $color-shadow-light;
a {
display: block;
height: 32px;
vertical-align: middle;
font-size: 14px;
@include text-shadow($color-shadow-light);
color: $color-text-dark;
padding: 5px;
.icon {
display: block;
float: left;
margin-right: 5px;
@include sprite-background("icons-32.png");
@include sprite-position(1, 1);
}
.text {
display: block;
padding-top: 10px;
}
}
&.current {
background-color: $color-menu-button;
@include linear-gradient(color-stops(
$color-menu-button,
darken($color-menu-button, 20%)
));
li {
background-color: darken($color-menu-button, 5%);
@include linear-gradient(color-stops(
darken($color-menu-button, 5%),
darken($color-menu-button, 10%)
));
}
a {
color: $color-text-light;
@include text-shadow($color-shadow-dark);
}
}
// nested elements
li {
&:first {
@include box-shadow($color-shadow-dark, 0, 4px, 4px);
}
a {
font-size: 12px;
@include text-shadow($color-shadow-dark);
margin: 0;
padding-left: 30px;
color: $color-text-light;
}
&.current a {
font-weight: bold;
}
}
}
li#Menu-CMSMain a .icon {@include sprite-position(1, 1);}
li#Menu-CMSMain.current a .icon, li#Menu-CMSMain a:hover .icon {@include sprite-position(2, 1);}
li#Menu-AssetAdmin a .icon {@include sprite-position(1, 2);}
li#Menu-AssetAdmin.current a .icon, li#Menu-AssetAdmin a:hover .icon {@include sprite-position(2, 2);}
}

299
admin/scss/_style.scss Normal file
View File

@ -0,0 +1,299 @@
/**
* This file defines the 'theme' of the CMS: Colors, fonts, backgrounds, and detailed alignments.
* Together with _layout.css it provides the presentational backbone to the CMS.
* Ideally a developer should be able to exchange this file with his own theme easily.
*
* Please don't put any dimension, display or float information on major structural components
* like '.cms-container' or '.cms-header' in here, use the _layout.scss file instead.
*
* Use SCSS variable definitions in screen.css to avoid repeating styles like background colours
* or padding dimensions. See _colours.scss to get started.
*
* To avoid this file getting too large and complicated, it is encouraged to create new SCSS files
* for larger components like the CMS menu or tree (see _tree.scss and _menu.scss).
*/
// ######################### Base styles #########################
body {
@include global-reset;
font-size: 13px;
font-family: Verdana, Arial, sans-serif;
color: $color-text;
}
body * {
font-size: 13px;
}
body .ui-widget {
font-size: 1em;
}
strong {
font-weight: bold;
}
// ######################### Misc Panels #########################
.cms-content-header {
background-color: darken($color-widget-bg, 20%);
@include linear-gradient(color-stops(
darken($color-widget-bg, 20%),
darken($color-widget-bg, 30%)
));
h2 {
float: left;
padding: 10px;
font-size: 14px;
font-weight: bold;
}
.cms-content-header-tabs {
float: left;
}
}
.ui-tabs .cms-content-header {
.ui-tabs-nav li {
height: 40px;
a {
font-weight: bold;
font-size: 11px;
padding-top: 1em;
}
}
.ui-state-default,
.ui-widget-content .ui-state-default,
.ui-widget-header .ui-state-default {
background-color: lighten($color-shadow-light, 20%);
@include linear-gradient(color-stops(
lighten($color-shadow-light, 20%),
lighten($color-shadow-light, 5%)
));
}
.ui-state-active,
.ui-widget-content .ui-state-active,
.ui-widget-header .ui-state-active {
background: $color-widget-bg;
}
}
.cms-content-tools {
background-color: darken($color-widget-bg, 5%);
}
// ######################### Header and Logo #########################
.cms-header {
padding: 10px;
background-color: darken($color-dark-bg, 10%);
@include linear-gradient(color-stops(
darken($color-dark-bg, 10%),
$color-dark-bg,
darken($color-dark-bg, 10%)
));
* {
color: $color-text-light;
}
}
.cms-logo {
.version {
display: none;
}
* {
color: $color-text-dark-link;
}
a {
display: inline-block;
height: 25px;
width: 25px;
float: left;
margin-right: 10px;
background: url(../images/logo_small.png) no-repeat;
text-indent: -9999em;
}
}
.cms-login-status {
.logout-link {
display: inline-block;
height: 25px;
width: 25px;
float: left;
margin-right: 10px;
background: url(../images/logout.png) no-repeat;
text-indent: -9999em;
}
}
// ######################### Loading Screen #########################
.ss-loading-screen,
.ss-loading-screen .loading-logo {
width: 100%;
height: 100%;
overflow: hidden;
position: absolute;
// TODO Convert to compass gradient include
background: #fff;
background: -moz-radial-gradient(50% 50% 180deg, circle cover, #FFFFFF, #EFEFEF, #C7C7C7 100%);
background: -webkit-gradient(radial, 50% 50%, 350, 50% 50%, 0, from(#E3E3E3), to(white));
z-index: 100000;
margin: 0;
padding: 0;
}
.ss-loading-screen {
.loading-logo {
background-url: url(../images/logo.gif);
background-repeat: no-repeat;
background-color: transparent;
background-position: 50% 50%;
}
p {
width: 100%;
text-align: center;
position: absolute;
bottom: 80px;
span.notice {
display: inline-block;
font-size: 14px;
padding: 10px 20px;
color: #dc7f00;
border: none;
@include border-radius(5px);
}
}
.loading-animation {
display: none;
position: absolute;
left: 49%;
top: 75%;
}
}
// ######################### Actions #########################
.cms-content-actions {
}
// ######################### Messages #########################
/**
* Messages (see sapphire/css/Form.css)
*/
.message {
margin: 1em 0;
padding: 0.5em;
font-weight: bold;
border: 1px black solid;
&.notice {
background-color: lighten($color-notice, 20%);
border-color: $color-notice;
}
&.warning {
background-color: lighten($color-warning, 20%);
border-color: $color-warning;
}
&.error {
background-color: lighten($color-error, 20%);
border-color: $color-error;
}
}
// ######################### ModelAdmin #########################
.ModelAdmin {
.cms-content-tools {
width: 300px;
}
// Disable by default, will be replaced by more intuitive column selection in new data grid
.ResultAssemblyBlock {
display: none;
}
}
// ######################### "Add page" dialog #########################
.cms-page-add-form-dialog {
#PageType li {
clear: left;
height: 40px;
border-bottom: 1px solid $color-shadow-dark;
&:hover, &.selected {
background-color: lighten($color-highlight, 10%);
}
input, label, .icon, .title {
float: left;
}
.icon {
width: 20px;
}
.title {
width: 100px;
font-weight: bold;
}
.description {
font-style: italic;
}
}
}
// ######################### Content toolbar #########################
.cms-content-toolbar {
& > * {
display: inline-block;
}
.cms-tree-view-modes * {
display: inline-block;
}
.cms-content-batchactions form * {
display: inline-block;
}
}
// ######################### Preview header (remove before release) #########################
.cms-preview-header {
background-color: #FFBE66;
padding: 10px;
font-weight: bold;
}
// ######################### Other #########################
.cms-preview {
background-color: $color-base;
.cms-preview-toggle {
cursor: pointer;
a {
color: $color-text-light;
font-weight: bold;
text-decoration: none;
}
}
}

366
admin/scss/_tree.scss Normal file
View File

@ -0,0 +1,366 @@
/**
* This file defines the jstree base styling (see http://jstree.com), as well as any
* customizations (see bottom of file).
*
* The styles are usually added through jstree.js on DOM load,
* but we need it earlier in order to correctly display the uninitialized tree.
*/
.jstree {
ul {
display: block;
margin: 0 0 0 0;
padding: 0 0 0 0;
list-style-type: none;
}
li {
display: block;
margin: 0 0 0 0;
padding: 0 0 0 0;
list-style-type: none;
display: block;
min-height: 18px;
line-height: 18px;
white-space: nowrap;
margin-left: 18px;
min-width: 18px;
}
ins {
display: inline-block;
text-decoration: none;
width: 18px;
height: 18px;
margin: 0 0 0 0;
padding: 0;
}
a {
display: inline-block;
line-height: 16px;
height: 16px;
color: black;
white-space: nowrap;
text-decoration: none;
padding: 1px 2px;
margin: 0;
&:focus {
outline: none;
}
> {
ins {
height: 16px;
width: 16px;
}
.jstree-icon {
margin-right: 3px;
}
}
}
li {
&.jstree-open > ul {
display: block;
}
&.jstree-closed > ul {
display: none;
}
}
li.disabled a {
color: #aaaaaa;
}
}
.jstree-rtl {
a > .jstree-icon {
margin-left: 3px;
margin-right: 0;
}
li {
margin-left: 0;
margin-right: 18px;
}
}
.jstree-rtl > ul > li {
margin-right: 0px;
}
.jstree > ul > li {
margin-left: 0px;
}
#vakata-dragged {
display: block;
margin: 0 0 0 0;
padding: 4px 4px 4px 24px;
position: absolute;
top: -2000px;
line-height: 16px;
z-index: 10000;
}
#vakata-contextmenu {
display: block;
visibility: hidden;
left: 0;
top: -200px;
position: absolute;
margin: 0;
padding: 0;
min-width: 180px;
background: #ebebeb;
border: 1px solid silver;
z-index: 10000; *width:180px;
}
#vakata-contextmenu ul {
min-width: 180px; *width:180px;
}
#vakata-contextmenu ul, #vakata-contextmenu li {
margin: 0;
padding: 0;
list-style-type: none;
display: block;
}
#vakata-contextmenu li {
line-height: 20px;
min-height: 20px;
position: relative;
padding: 0px;
}
#vakata-contextmenu li a {
padding: 1px 6px;
line-height: 17px;
display: block;
text-decoration: none;
margin: 1px 1px 0 1px;
}
#vakata-contextmenu li ins {
float: left;
width: 16px;
height: 16px;
text-decoration: none;
margin-right: 2px;
}
#vakata-contextmenu li a:hover, #vakata-contextmenu li.vakata-hover > a {
background: gray;
color: white;
}
#vakata-contextmenu li ul {
display: none;
position: absolute;
top: -2px;
left: 100%;
background: #ebebeb;
border: 1px solid gray;
}
#vakata-contextmenu .right {
right: 100%;
left: auto;
}
#vakata-contextmenu .bottom {
bottom: -1px;
top: auto;
}
#vakata-contextmenu li.vakata-separator {
min-height: 0;
height: 1px;
line-height: 1px;
font-size: 1px;
overflow: hidden;
margin: 0 2px;
background: silver; /* border-top:1px solid #fefefe; */ padding:0;
}
.jstree ul, .jstree li {
display: block;
margin: 0 0 0 0;
padding: 0 0 0 0;
list-style-type: none;
}
.jstree li {
display: block;
min-height: 18px;
line-height: 18px;
white-space: nowrap;
margin-left: 18px;
min-width: 18px;
}
.jstree-rtl li {
margin-left: 0;
margin-right: 18px;
}
.jstree > ul > li {
margin-left: 0px;
}
.jstree-rtl > ul > li {
margin-right: 0px;
}
.jstree ins {
display: inline-block;
text-decoration: none;
width: 18px;
height: 18px;
margin: 0 0 0 0;
padding: 0;
}
.jstree a {
display: inline-block;
line-height: 16px;
height: 16px;
color: black;
white-space: nowrap;
text-decoration: none;
padding: 1px 2px;
margin: 0;
}
.jstree a:focus {
outline: none;
}
.jstree a > ins {
height: 16px;
width: 16px;
}
.jstree a > .jstree-icon {
margin-right: 3px;
}
.jstree-rtl a > .jstree-icon {
margin-left: 3px;
margin-right: 0;
}
li.jstree-open > ul {
display: block;
}
li.jstree-closed > ul {
display: none;
}
#vakata-dragged ins {
display: block;
text-decoration: none;
width: 16px;
height: 16px;
margin: 0 0 0 0;
padding: 0;
position: absolute;
top: 4px;
left: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
-webkit-border-radius: 4px;
}
#vakata-dragged .jstree-ok {
background: green;
}
#vakata-dragged .jstree-invalid {
background: red;
}
#jstree-marker {
padding: 0;
margin: 0;
font-size: 12px;
overflow: hidden;
height: 12px;
width: 8px;
position: absolute;
top: -30px;
z-index: 10001;
background-repeat: no-repeat;
display: none;
background-color: transparent;
text-shadow: 1px 1px 1px white;
color: black;
line-height: 10px;
}
#jstree-marker-line {
padding: 0;
margin: 0;
line-height: 0%;
font-size: 1px;
overflow: hidden;
height: 1px;
width: 100px;
position: absolute;
top: -30px;
z-index: 10000;
background-repeat: no-repeat;
display: none;
background-color: #456c43;
cursor: pointer;
border: 1px solid #eeeeee;
border-left: 0;
-moz-box-shadow: 0px 0px 2px #666;
-webkit-box-shadow: 0px 0px 2px #666;
box-shadow: 0px 0px 2px #666;
-moz-border-radius: 1px;
border-radius: 1px;
-webkit-border-radius: 1px;
}
.jstree .jstree-real-checkbox {
display: none;
}
.jstree-themeroller .ui-icon {
overflow: visible;
}
.jstree-themeroller a {
padding: 0 2px;
}
.jstree-themeroller .jstree-no-icon {
display: none;
}
.jstree .jstree-wholerow-real {
position: relative;
z-index: 1;
}
.jstree .jstree-wholerow-real li {
cursor: pointer;
}
.jstree .jstree-wholerow-real a {
border-left-color: transparent !important;
border-right-color: transparent !important;
}
.jstree .jstree-wholerow {
position: relative;
z-index: 0;
height: 0;
}
.jstree .jstree-wholerow ul, .jstree .jstree-wholerow li {
width: 100%;
}
.jstree .jstree-wholerow, .jstree .jstree-wholerow ul, .jstree .jstree-wholerow li, .jstree .jstree-wholerow a {
margin: 0 !important;
padding: 0 !important;
}
.jstree .jstree-wholerow, .jstree .jstree-wholerow ul, .jstree .jstree-wholerow li {
background: transparent !important;
}
.jstree .jstree-wholerow ins, .jstree .jstree-wholerow span, .jstree .jstree-wholerow input {
display: none !important;
}
.jstree .jstree-wholerow a, .jstree .jstree-wholerow a:hover {
text-indent: -9999px !important;
width: 100%;
padding: 0 !important;
border-right-width: 0px !important;
border-left-width: 0px !important;
}
.jstree .jstree-wholerow-span {
position: absolute;
left: 0;
margin: 0px;
padding: 0;
height: 18px;
border-width: 0;
padding: 0;
z-index: 0;
}
// Custom styles
.cms {
.jstree-apple.jstree-focused {
background: none;
}
.jstree-apple > ul {
background: none;
}
}

40
admin/scss/_uitheme.scss Normal file
View File

@ -0,0 +1,40 @@
/**
* This file defines CMS-specific customizations to the jQuery UI theme.
* Every rule in this file should be wrapped in the '.cms' selector (to make it more specific),
* and contain ONLY overwritten jQuery UI rules (with 'ui-' prefix).
*
* This file should be fairly short, as we're using our own custom jQuery UI theme already.
* TODO Add theme reference
*
* Use _style.scss to add more generic style information,
* and read the jQuery UI theming API: http://jqueryui.com/docs/Theming/API
*/
.cms {
.ui-tabs {
padding: 0;
.ui-widget-header {
border: 0;
background: none;
}
}
.ui-widget-content,
.ui-tabs .ui-tabs-panel {
color: $color-text;
font-size: 1em;
border: 0;
background: $color-widget-bg;
}
.ui-widget-header {
background: $color-widget-bg;
border: 0;
}
.ss-ui-button {
padding: 5px;
text-decoration: none;
}
}

20
admin/scss/screen.scss Normal file
View File

@ -0,0 +1,20 @@
/**
* This file is the central collection of included modules, links to custom SCSS files,
* and any global SCSS variable definitions.
*
* DO NOT ADD stylesheet rules to this file directly!
*
* Note: By prefixing files with an underscore, they won't create individual CSS files.
*/
@import "compass/reset";
@import "compass/css3";
@import "compass/utilities/sprites/sprite-img";
@import "colours.scss";
@import "layout.scss";
@import "tree.scss";
@import "menu.scss";
@import "forms.scss";
@import "uitheme.scss";
@import "style.scss";

View File

@ -0,0 +1,16 @@
<div id="$id">
<%-- Tab nav is rendered in CMSEditForm.ss --%>
<% control Tabs %>
<div class="tab" id="$id">
<% if Tabs %>
$FieldHolder
<% else %>
<% control Fields %>
$FieldHolder
<% end_control %>
<% end_if %>
</div>
<% end_control %>
</div>

View File

@ -0,0 +1,6 @@
<div class="ss-loading-screen">
<div class="loading-logo">
<img class="loading-animation" src="sapphire/admin/images/spinner.gif" alt="<% _t('LOADING','Loading...',PR_HIGH) %>" />
<noscript><p class="nojs-warning"><span class="message notice"><% _t('REQUIREJS','The CMS requires that you have JavaScript enabled.',PR_HIGH) %></span></p></noscript>
</div>
</div>

View File

@ -1,10 +0,0 @@
<div id="Logo" style="$LogoStyle">
<% if ApplicationLogoText %>
<a href="$ApplicationLink" target="_blank">$ApplicationLogoText</a><br />
<% end_if %>
</div>
<ul id="MainMenu">
<% control MainMenu %>
<li class="$LinkingMode" id="Menu-$Code"><a href="$Link">$Title</a></li>
<% end_control %>
</ul>

View File

@ -0,0 +1 @@
$EditForm

View File

@ -0,0 +1,58 @@
<% if IncludeFormTag %>
<form $FormAttributes data-layout="{type: 'border'}">
<% end_if %>
<div class="cms-content-header north">
<div>
<h2>My Page Title</h2>
<% if Fields.hasTabset %>
<% with Fields.fieldByName('Root') %>
<div class="cms-content-header-tabs">
<ul>
<% control Tabs %>
<li><a href="#$id">$Title</a></li>
<% end_control %>
</ul>
<% end_with %>
</div>
<% end_if %>
<!-- <div class="cms-content-search">...</div> -->
</div>
</div>
<!-- <div class="cms-content-tools west">
$Left
</div> -->
<div class="cms-content-form center">
<% if Message %>
<p id="{$FormName}_error" class="message $MessageType">$Message</p>
<% else %>
<p id="{$FormName}_error" class="message $MessageType" style="display: none"></p>
<% end_if %>
<fieldset>
<% if Legend %><legend>$Legend</legend><% end_if %>
<% control Fields %>
$FieldHolder
<% end_control %>
<div class="clear"><!-- --></div>
</fieldset>
</div>
<div class="cms-content-actions south">
<% if Actions %>
<div class="Actions">
<% control Actions %>
$Field
<% end_control %>
</div>
<% end_if %>
</div>
<% if IncludeFormTag %>
</form>
<% end_if %>

View File

@ -0,0 +1,50 @@
<div class="cms-menu west">
<div class="cms-header">
<div class="cms-logo">
<a href="http://www.silverstripe.org/" target="_blank">
SilverStripe <% if CMSVersion %><abbr class="version">$CMSVersion</abbr><% end_if %>
</a>
<span>$SiteConfig.Title</span>
</div>
<div class="cms-login-status">
<a href="Security/logout" class="logout-link"><% _t('LOGOUT','Log out') %></a>
<% control CurrentMember %>
<% _t('Hello','Hi') %>
<strong>
<a href="{$AbsoluteBaseURL}admin/myprofile" class="profile-link">
<% if FirstName && Surname %>$FirstName $Surname<% else_if FirstName %>$FirstName<% else %>$Email<% end_if %>
</a>
</strong>
<% end_control %>
</div>
</div>
<ul class="cms-menu-list">
<% control MainMenu %>
<li class="$LinkingMode" id="Menu-$Code">
<a href="$Link">
<span class="icon">&nbsp;</span>
<span class="text">$Title</span>
</a>
<% if Code == 'CMSMain' && LinkingMode == 'current' %>
<ul>
<li <% if Top.class == 'CMSPageEditController' %>class="current"<% end_if %>><a href="admin/page/edit/show/$Top.CurrentPageID">
<span class="text">Content</span>
</a></li>
<li <% if Top.class == 'CMSPageSettingsController' %>class="current"<% end_if %>><a href="admin/page/settings/show/$Top.CurrentPageID">
<span class="text">Settings</span>
</a></li>
<li <% if Top.class == 'CMSPageReportsController' %>class="current"<% end_if %>><a href="#">
<span class="text">Reports</span>
</a></li>
<li <% if Top.class == 'CMSPageHistoryController' %>class="current"<% end_if %>><a href="#">
<span class="text">History</span>
</a></li>
</ul>
<% end_if %>
</li>
<% end_control %>
</ul>
</div>

View File

@ -1,3 +0,0 @@
$EditForm
<div class="notice-wrap"></div>

View File

@ -0,0 +1,45 @@
<div class="cms-content center" data-layout="{type: 'border'}">
<div class="cms-content-header north">
<div>
<h2><% _t('ModelAdmin.Title', 'My Model') %></h2>
</div>
</div>
<div class="cms-content-tools west">
<div id="SearchForm_holder" class="leftbottom ss-tabset">
<% if SearchClassSelector = tabs %>
<ul>
<% control ModelForms %>
<li class="$FirstLast"><a id="tab-ModelAdmin_$Title.HTMLATT" href="#{$Form.Name}_$ClassName">$Title</a></li>
<% end_control %>
</ul>
<% end_if %>
<% if SearchClassSelector = dropdown %>
<div id="ModelClassSelector" class="ui-widget-container">
Search for:
<select>
<% control ModelForms %>
<option value="{$Form.Name}_$ClassName">$Title</option>
<% end_control %>
</select>
</div>
<% end_if %>
<% control ModelForms %>
<div class="tab" id="{$Form.Name}_$ClassName">
$Content
</div>
<% end_control %>
</div>
</div>
<div class="cms-content-form center ui-widget-content">
$EditForm
</div>
</div>

View File

@ -0,0 +1 @@
<% include Form %>

View File

@ -1,26 +0,0 @@
<div id="SearchForm_holder" class="leftbottom ss-tabset ui-layout-content">
<% if SearchClassSelector = tabs %>
<ul>
<% control ModelForms %>
<li class="$FirstLast"><a id="tab-ModelAdmin_$Title.HTMLATT" href="#{$Form.Name}_$ClassName">$Title</a></li>
<% end_control %>
</ul>
<% end_if %>
<% if SearchClassSelector = dropdown %>
<p id="ModelClassSelector">
Search for:
<select>
<% control ModelForms %>
<option value="{$Form.Name}_$ClassName">$Title</option>
<% end_control %>
</select>
</p>
<% end_if %>
<% control ModelForms %>
<div class="tab" id="{$Form.Name}_$ClassName">
$Content
</div>
<% end_control %>
</div>

View File

@ -0,0 +1,38 @@
<div class="cms-content center" data-layout="{type: 'border'}">
<div class="cms-content-header north">
<div>
<h2><% _t('SECGROUPS','Security Groups') %></h2>
</div>
</div>
<div class="cms-content-tools west">
<div id="treepanes">
<div>
$AddForm
<div class="checkboxAboveTree">
<input type="checkbox" id="sortitems" />
<label for="sortitems">
<% _t('ENABLEDRAGGING','Allow drag &amp; drop reordering', PR_HIGH) %>
</label>
</div>
<div data-url-tree="$Link(getsubtree)" data-url-savetreenode="$Link(savetreenode)" class="cms-tree jstree jstree-apple">
$SiteTreeAsUL
</div>
</div>
</div>
</div>
<div class="cms-content-form center ui-widget-content">
$EditForm
</div>
</div>

View File

@ -1,45 +0,0 @@
<div id="treepanes">
<h3>
<a href="#"><% _t('SECGROUPS','Security Groups') %></a>
</h3>
<div>
<div id="TreeActions">
<ul>
<li>
<a href="#TreeActions-create" id="TreeActions-create-btn">
<% _t('CREATE','Create',PR_HIGH) %>
</a>
</li>
<li>
<a href="#TreeActions-batchactions" id="batchactions">
<% _t('BATCHACTIONS','Batch Actions',PR_HIGH) %>
</a>
</li>
</ul>
<div id="TreeActions-create">
$AddForm
</div>
<div id="TreeActions-batchactions">
$BatchActionsForm
</div>
</div>
<div class="checkboxAboveTree">
<input type="checkbox" id="sortitems" />
<label for="sortitems">
<% _t('ENABLEDRAGGING','Allow drag &amp; drop reordering', PR_HIGH) %>
</label>
</div>
<div id="sitetree_ul" data-url-tree="$Link(getsubtree)" data-url-savetreenode="$Link(savetreenode)" class="jstree jstree-apple">
$SiteTreeAsUL
</div>
</div>
</div>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"> <html>
<head> <head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-language" content="$i18nLocale" /> <meta http-equiv="Content-language" content="$i18nLocale" />
@ -7,27 +7,33 @@
<title>$ApplicationName | $SectionTitle</title> <title>$ApplicationName | $SectionTitle</title>
</head> </head>
<body class="stillLoading $CSSClasses"> <body class="loading cms $CSSClasses">
<div class="ss-loading-screen">
<div class="loading-logo" style="background-image: url($LoadingImage);"> <% include CMSLoadingScreen %>
<img class="loading-animation" src="sapphire/admin/images/spinner.gif" alt="<% _t('LOADING','Loading...',PR_HIGH) %>" />
<noscript><p class="nojs-warning"><span class="message notice"><% _t('REQUIREJS','The CMS requires that you have JavaScript enabled.',PR_HIGH) %></span></p></noscript> <div class="cms-container center" data-layout="{type: 'border'}">
</div>
<div class="cms-preview-header north">
Caution! The CMS is in alpha stage, and might not behave as expected. Get updates on the
<a href="https://groups.google.com/forum/#!forum/silverstripe-dev">developer mailinglist</a>
and help us by
<a href="http://silverstripe.org/contribute">contributing</a> and
<a href="http://open.silverstripe.org">reporting bugs</a>.
</div> </div>
<div class="ui-layout-north ss-cms-top-menu"> $Menu
$CMSTopMenu
$Content
<% if currentPage %>
<div class="cms-preview east">
<iframe src="$currentPage.Link"></iframe>
</div>
<% end_if %>
</div> </div>
<div class="ui-layout-west"> <div id="cms-editor-dialogs">
$Left
</div>
<div class="ui-layout-center right" id="right">
$Right
</div>
<div class="ui-layout-east" id="contentPanel">
<% control EditorToolbar %> <% control EditorToolbar %>
$ImageForm $ImageForm
$LinkForm $LinkForm
@ -35,19 +41,8 @@
<% end_control %> <% end_control %>
</div> </div>
<div class="ui-layout-south ss-cms-bottom-bar"> <!-- <div class="ss-cms-bottom-bar">
<div class="holder"> <div class="holder">
<div id="logInStatus">
<a href="$ApplicationLink" title="<% _t('SSWEB','Silverstripe Website') %>">$ApplicationName</a>
<% if CMSVersion %>-&nbsp;
<abbr style="border-style: none" title="<% _t('APPVERSIONTEXT1',"This is the") %> $ApplicationName <% _t('APPVERSIONTEXT2',"version that you are currently running, technically it's the CVS branch") %>">$CMSVersion</abbr>
<% end_if %>
&nbsp;&nbsp;
<% control CurrentMember %>
<% _t('LOGGEDINAS','Logged in as') %> <strong><% if FirstName && Surname %>$FirstName $Surname<% else_if FirstName %>$FirstName<% else %>$Email<% end_if %></strong> | <a href="{$AbsoluteBaseURL}admin/myprofile" id="EditMemberProfile"><% _t('EDITPROFILE','Profile') %></a> | <a href="Security/logout" id="LogoutLink"><% _t('LOGOUT','Log out') %></a>
<% end_control %>
</div>
<div id="switchView" class="bottomTabs"> <div id="switchView" class="bottomTabs">
<% if ShowSwitchView %> <% if ShowSwitchView %>
<div class="blank"> <% _t('VIEWPAGEIN','Page view:') %> </div> <div class="blank"> <% _t('VIEWPAGEIN','Page view:') %> </div>
@ -55,6 +50,7 @@
<% end_if %> <% end_if %>
</div> </div>
</div> </div>
</div> </div> -->
</body> </body>
</html> </html>

View File

@ -1,5 +1,4 @@
<% if CreateForm %> <% if CreateForm %>
<h3><% _t('ADDLISTING','Add') %></h3>
$CreateForm $CreateForm
<% end_if %> <% end_if %>

8
admin/thirdparty/jlayout/.piston.yml vendored Normal file
View File

@ -0,0 +1,8 @@
---
format: 1
handler:
commit: b5ff32977e136f519236d29593433e87dadb74d0
branch: master
lock: false
repository_class: Piston::Git::Repository
repository_url: https://github.com/bramstein/jlayout.git

View File

@ -0,0 +1,136 @@
/**
* @preserve jLayout Border Layout - JavaScript Layout Algorithms v0.4
*
* Licensed under the new BSD License.
* Copyright 2008-2009, Bram Stein
* All rights reserved.
*/
/*global jLayout:true */
(function () {
jLayout = (typeof jLayout === 'undefined') ? {} : jLayout;
jLayout.border = function (spec) {
var my = {},
that = {},
east = spec.east,
west = spec.west,
north = spec.north,
south = spec.south,
center = spec.center;
my.hgap = spec.hgap || 0;
my.vgap = spec.vgap || 0;
that.items = function () {
var items = [];
if (east) {
items.push(east);
}
if (west) {
items.push(west);
}
if (north) {
items.push(north);
}
if (south) {
items.push(south);
}
if (center) {
items.push(center);
}
return items;
};
that.layout = function (container) {
var size = container.bounds(),
insets = container.insets(),
top = insets.top,
bottom = size.height - insets.bottom,
left = insets.left,
right = size.width - insets.right,
tmp;
if (north && north.isVisible()) {
tmp = north.preferredSize();
north.bounds({'x': left, 'y': top, 'width': right - left, 'height': tmp.height});
north.doLayout();
top += tmp.height + my.vgap;
}
if (south && south.isVisible()) {
tmp = south.preferredSize();
south.bounds({'x': left, 'y': bottom - tmp.height, 'width': right - left, 'height': tmp.height});
south.doLayout();
bottom -= tmp.height + my.vgap;
}
if (east && east.isVisible()) {
tmp = east.preferredSize();
east.bounds({'x': right - tmp.width, 'y': top, 'width': tmp.width, 'height': bottom - top});
east.doLayout();
right -= tmp.width + my.hgap;
}
if (west && west.isVisible()) {
tmp = west.preferredSize();
west.bounds({'x': left, 'y': top, 'width': tmp.width, 'height': bottom - top});
west.doLayout();
left += tmp.width + my.hgap;
}
if (center && center.isVisible()) {
center.bounds({'x': left, 'y': top, 'width': right - left, 'height': bottom - top});
center.doLayout();
}
return container;
};
function typeLayout(type) {
return function (container) {
var insets = container.insets(),
width = 0,
height = 0,
type_size;
if (east && east.isVisible()) {
type_size = east[type + 'Size']();
width += type_size.width + my.hgap;
height = type_size.height;
}
if (west && west.isVisible()) {
type_size = west[type + 'Size']();
width += type_size.width + my.hgap;
height = Math.max(type_size.height, height);
}
if (center && center.isVisible()) {
type_size = center[type + 'Size']();
width += type_size.width;
height = Math.max(type_size.height, height);
}
if (north && north.isVisible()) {
type_size = north[type + 'Size']();
width = Math.max(type_size.width, width);
height += type_size.height + my.vgap;
}
if (south && south.isVisible()) {
type_size = south[type + 'Size']();
width = Math.max(type_size.width, width);
height += type_size.height + my.vgap;
}
return {
'width': width + insets.left + insets.right,
'height': height + insets.top + insets.bottom
};
};
}
that.preferred = typeLayout('preferred');
that.minimum = typeLayout('minimum');
that.maximum = typeLayout('maximum');
return that;
};
}());

View File

@ -0,0 +1,88 @@
/**
* @preserve jLayout Column Layout - JavaScript Layout Algorithms v0.1
*
* Licensed under the new BSD License.
* Copyright 2008-2009, Bram Stein
* All rights reserved.
*/
/*global jLayout */
/*
(function () {
jLayout = typeof jLayout === 'undefined' ? {} : jLayout;
jLayout.column = function (options) {
var that = {},
my = {};
my.hgap = options.hgap || 0;
my.vgap = options.vgap || 0;
my.columns = options.columns || 2;
my.items = options.items || [];
my.maxHeight = options.maxHeight || -1;
that.items = function () {
var r = [];
Array.prototype.push.apply(r, my.items);
return r;
};
that.layout = function (container) {
var i = 0, j = 1,
insets = container.insets(),
x = insets.left,
y = insets.top,
rows = 0,
width = (container.bounds().width - (insets.left + insets.right) - (my.columns - 1) * my.hgap) / my.columns,
// TODO: if maxHeight is not available the height should be the height that divides the content equally over all columns.
height = my.maxHeight,
itemSize;
// container.bounds({'height': height * 4});
console.log(height);
for (; i < my.items.length; i += 1) {
my.items[i].bounds({'width': width});
}
for (i = 0; i < my.items.length; i += 1) {
itemSize = my.items[i].preferredSize();
if (y + itemSize.height + my.hgap > height) {
if (j === my.columns) {
rows += 1;
y = insets.top + my.hgap + height * rows;
x = insets.left;
j = 1;
} else {
y = insets.top + (rows * height);
x += width + my.vgap;
j += 1;
}
}
my.items[i].bounds({'x': x, 'y': y});
y += itemSize.height + my.hgap;
my.items[i].doLayout();
}
//console.log(width);
return container;
};
function typeLayout(type) {
return function (container) {
return {
width: 800,
height: 600
};
};
}
that.preferred = typeLayout('preferred');
that.minimum = typeLayout('minimum');
that.maximum = typeLayout('maximum');
return that;
};
})();*/

View File

@ -0,0 +1,108 @@
/**
* @preserve jLayout Flex Grid Layout - JavaScript Layout Algorithms v0.4
* Based on: http://www.javaworld.com/javaworld/javatips/jw-javatip121.html
*
* Licensed under the new BSD License.
* Copyright 2008-2009, Bram Stein
* All rights reserved.
*/
/*global jLayout:true */
(function () {
jLayout = (typeof jLayout === 'undefined') ? {} : jLayout;
// The flex grid has a dependency on the grid layout, so please make
// sure you include the grid layout manager before the flex grid
// layout manager.
if (typeof jLayout.grid !== 'undefined') {
jLayout.flexGrid = function (spec) {
var my = {},
that = this.grid(spec, my);
function zeroArray(a, l) {
var i = 0;
for (; i < l; i += 1) {
a[i] = 0;
}
return a;
}
function typeLayout(type) {
return function (container) {
var i = 0, r = 0, c = 0, nw = 0, nh = 0,
w = zeroArray([], my.columns),
h = zeroArray([], my.rows),
type_size,
insets = container.insets();
for (i = 0; i < my.items.length; i += 1) {
r = Math.floor(i / my.columns);
c = i % my.columns;
type_size = my.items[i][type + 'Size']();
if (w[c] < type_size.width) {
w[c] = type_size.width;
}
if (h[r] < type_size.height) {
h[r] = type_size.height;
}
}
for (i = 0; i < my.columns; i += 1) {
nw += w[i];
}
for (i = 0; i < my.rows; i += 1) {
nh += h[i];
}
return {
width: insets.left + insets.right + nw + (my.columns - 1) * my.hgap,
height: insets.top + insets.bottom + nh + (my.rows - 1) * my.vgap
};
};
}
that.preferred = typeLayout('preferred');
that.minimum = typeLayout('minimum');
that.maximum = typeLayout('maximum');
that.layout = function (container) {
var i = 0, c = 0, r = 0,
pd = that.preferred(container),
sw = container.bounds().width / pd.width,
sh = container.bounds().height / pd.height,
w = zeroArray([], my.columns),
h = zeroArray([], my.rows),
insets = container.insets(),
x = insets.left,
y = insets.top,
d;
for (i = 0; i < my.items.length; i += 1) {
r = Math.floor(i / my.columns);
c = i % my.columns;
d = my.items[i].preferredSize();
d.width = sw * d.width;
d.height = sh * d.height;
if (w[c] < d.width) {
w[c] = d.width;
}
if (h[r] < d.height) {
h[r] = d.height;
}
}
for (c = 0; c < my.columns; c += 1) {
for (r = 0, y = insets.top; r < my.rows; r += 1) {
i = r * my.columns + c;
if (i < my.items.length) {
my.items[i].bounds({'x': x, 'y': y, 'width': w[c], 'height': h[r]});
my.items[i].doLayout();
}
y += h[r] + my.vgap;
}
x += w[c] + my.hgap;
}
return container;
};
return that;
};
}
}());

View File

@ -0,0 +1,127 @@
/**
* @preserve jLayout Flow Layout - JavaScript Layout Algorithms v0.12
*
* Licensed under the new BSD License.
* Copyright 2008-2009, Bram Stein
* All rights reserved.
*/
/*global jLayout:true */
(function () {
jLayout = (typeof jLayout === 'undefined') ? {} : jLayout;
jLayout.flow = function (options) {
var my = {},
that = {};
my.hgap = typeof options.hgap === 'number' && !isNaN(options.hgap) ? options.hgap : 5;
my.vgap = typeof options.vgap === 'number' && !isNaN(options.vgap) ? options.vgap : 5;
my.items = options.items || [];
my.alignment = (options.alignment && (options.alignment === 'center' || options.alignment === 'right' || options.alignment === 'left') && options.alignment) || 'left';
that.items = function () {
var r = [];
Array.prototype.push.apply(r, my.items);
return r;
};
function align(row, offset, rowSize, parentSize) {
var location = {
x: offset.x,
y: offset.y
},
i = 0,
len = row.length;
switch (my.alignment) {
case 'center':
location.x += (my.hgap + parentSize.width - rowSize.width) / 2;
break;
case 'right':
location.x += parentSize.width - rowSize.width + my.hgap;
break;
}
for (; i < len; i += 1) {
location.y = offset.y;
row[i].bounds(location);
row[i].doLayout();
location.x += row[i].bounds().width + my.hgap;
}
}
that.layout = function (container) {
var parentSize = container.bounds(),
insets = container.insets(),
i = 0,
len = my.items.length,
itemSize,
currentRow = [],
rowSize = {
width: 0,
height: 0
},
offset = {
x: insets.left,
y: insets.top
};
parentSize.width -= insets.left + insets.right;
parentSize.height -= insets.top + insets.bottom;
for (; i < len; i += 1) {
if (my.items[i].isVisible()) {
itemSize = my.items[i].preferredSize();
if ((rowSize.width + itemSize.width) > parentSize.width) {
align(currentRow, offset, rowSize, parentSize);
currentRow = [];
offset.y += rowSize.height;
offset.x = insets.left;
rowSize.width = 0;
rowSize.height = 0;
}
rowSize.height = Math.max(rowSize.height, itemSize.height + my.vgap);
rowSize.width += itemSize.width + my.hgap;
currentRow.push(my.items[i]);
}
}
align(currentRow, offset, rowSize, parentSize);
return container;
};
function typeLayout(type) {
return function (container) {
var i = 0,
width = 0,
height = 0,
typeSize,
firstComponent = false,
insets = container.insets();
for (; i < my.items.length; i += 1) {
if (my.items[i].isVisible()) {
typeSize = my.items[i][type + 'Size']();
height = Math.max(height, typeSize.height);
width += typeSize.width;
}
}
return {
'width': width + insets.left + insets.right + (my.items.length - 1) * my.hgap,
'height': height + insets.top + insets.bottom
};
};
}
that.preferred = typeLayout('preferred');
that.minimum = typeLayout('minimum');
that.maximum = typeLayout('maximum');
return that;
};
}());

View File

@ -0,0 +1,99 @@
/**
* @preserve jLayout Grid Layout - JavaScript Layout Algorithms v0.41
*
* Licensed under the new BSD License.
* Copyright 2008-2009, Bram Stein
* All rights reserved.
*/
/*global jLayout:true */
(function () {
jLayout = (typeof jLayout === 'undefined') ? {} : jLayout;
jLayout.grid = function (spec, shared) {
var my = shared || {},
that = {};
my.hgap = spec.hgap || 0;
my.vgap = spec.vgap || 0;
// initialize the number of columns to the number of items
// we're laying out.
my.items = spec.items || [];
my.columns = spec.columns || my.items.length;
my.rows = spec.rows || 0;
my.fillVertical = spec.fill && spec.fill === 'vertical';
if (my.rows > 0) {
my.columns = Math.floor((my.items.length + my.rows - 1) / my.rows);
} else {
my.rows = Math.floor((my.items.length + my.columns - 1) / my.columns);
}
that.items = function () {
var r = [];
Array.prototype.push.apply(r, my.items);
return r;
};
that.layout = function (container) {
var i, j,
insets = container.insets(),
x = insets.left,
y = insets.top,
width = (container.bounds().width - (insets.left + insets.right) - (my.columns - 1) * my.hgap) / my.columns,
height = (container.bounds().height - (insets.top + insets.bottom) - (my.rows - 1) * my.vgap) / my.rows;
for (i = 0, j = 1; i < my.items.length; i += 1, j += 1) {
my.items[i].bounds({'x': x, 'y': y, 'width': width, 'height': height});
if (!my.fillVertical) {
if (j >= my.columns) {
y += height + my.vgap;
x = insets.left;
j = 0;
}
else {
x += width + my.hgap;
}
} else {
if (j >= my.rows) {
x += width + my.hgap;
y = insets.top;
j = 0;
} else {
y += height + my.vgap;
}
}
my.items[i].doLayout();
}
return container;
};
function typeLayout(type) {
return function (container) {
var i = 0,
width = 0,
height = 0,
type_size,
insets = container.insets();
for (; i < my.items.length; i += 1) {
type_size = my.items[i][type + 'Size']();
width = Math.max(width, type_size.width);
height = Math.max(height, type_size.height);
}
return {
'width': insets.left + insets.right + my.columns * width + (my.columns - 1) * my.hgap,
'height': insets.top + insets.bottom + my.rows * height + (my.rows - 1) * my.vgap
};
};
}
// this creates the min and preferred size methods, as they
// only differ in the function they call.
that.preferred = typeLayout('preferred');
that.minimum = typeLayout('minimum');
that.maximum = typeLayout('maximum');
return that;
};
}());

182
admin/thirdparty/jlayout/lib/jquery.jlayout.js vendored Executable file
View File

@ -0,0 +1,182 @@
/**
* @preserve jLayout JQuery Plugin v0.17
*
* Licensed under the new BSD License.
* Copyright 2008-2009 Bram Stein
* All rights reserved.
*/
/*global jQuery jLayout*/
if (jQuery && jLayout) {
(function ($) {
/**
* This wraps jQuery objects in another object that supplies
* the methods required for the layout algorithms.
*/
function wrap(item, resize) {
var that = {};
$.each(['min', 'max'], function (i, name) {
that[name + 'imumSize'] = function (value) {
var l = item.data('jlayout');
if (l) {
return l[name + 'imum'](that);
} else {
return item[name + 'Size'](value);
}
};
});
$.extend(that, {
doLayout: function () {
var l = item.data('jlayout');
if (l) {
l.layout(that);
}
item.css({position: 'absolute'});
},
isVisible: function () {
return item.isVisible();
},
insets: function () {
var p = item.padding(),
b = item.border();
return {
'top': p.top,
'bottom': p.bottom + b.bottom + b.top,
'left': p.left,
'right': p.right + b.right + b.left
};
},
bounds: function (value) {
var tmp = {};
if (value) {
if (typeof value.x === 'number') {
tmp.left = value.x;
}
if (typeof value.y === 'number') {
tmp.top = value.y;
}
if (typeof value.width === 'number') {
tmp.width = (value.width - (item.outerWidth(true) - item.width()));
tmp.width = (tmp.width >= 0) ? tmp.width : 0;
}
if (typeof value.height === 'number') {
tmp.height = value.height - (item.outerHeight(true) - item.height());
tmp.height = (tmp.height >= 0) ? tmp.height : 0;
}
item.css(tmp);
return item;
} else {
tmp = item.position();
return {
'x': tmp.left,
'y': tmp.top,
'width': item.outerWidth(false),
'height': item.outerHeight(false)
};
}
},
preferredSize: function () {
var minSize,
maxSize,
margin = item.margin(),
size = {width: 0, height: 0},
l = item.data('jlayout');
if (l && resize) {
size = l.preferred(that);
minSize = that.minimumSize();
maxSize = that.maximumSize();
size.width += margin.left + margin.right;
size.height += margin.top + margin.bottom;
if (size.width < minSize.width || size.height < minSize.height) {
size.width = Math.max(size.width, minSize.width);
size.height = Math.max(size.height, minSize.height);
} else if (size.width > maxSize.width || size.height > maxSize.height) {
size.width = Math.min(size.width, maxSize.width);
size.height = Math.min(size.height, maxSize.height);
}
} else {
size = that.bounds();
size.width += margin.left + margin.right;
size.height += margin.top + margin.bottom;
}
return size;
}
});
return that;
}
$.fn.layout = function (options) {
var opts = $.extend({}, $.fn.layout.defaults, options);
return $.each(this, function () {
var element = $(this),
o = $.metadata && element.metadata().layout ? $.extend(opts, element.metadata().layout) : opts,
elementWrapper = wrap(element, o.resize);
if (o.type === 'border' && typeof jLayout.border !== 'undefined') {
$.each(['north', 'south', 'west', 'east', 'center'], function (i, name) {
if (element.children().hasClass(name)) {
o[name] = wrap(element.children('.' + name + ':first'));
}
});
element.data('jlayout', jLayout.border(o));
} else if (o.type === 'grid' && typeof jLayout.grid !== 'undefined') {
o.items = [];
element.children().each(function (i) {
if (!$(this).hasClass('ui-resizable-handle')) {
o.items[i] = wrap($(this));
}
});
element.data('jlayout', jLayout.grid(o));
} else if (o.type === 'flexGrid' && typeof jLayout.flexGrid !== 'undefined') {
o.items = [];
element.children().each(function (i) {
if (!$(this).hasClass('ui-resizable-handle')) {
o.items[i] = wrap($(this));
}
});
element.data('jlayout', jLayout.flexGrid(o));
} else if (o.type === 'column' && typeof jLayout.column !== 'undefined') {
o.items = [];
element.children().each(function (i) {
if (!$(this).hasClass('ui-resizable-handle')) {
o.items[i] = wrap($(this));
}
});
element.data('jlayout', jLayout.column(o));
} else if (o.type === 'flow' && typeof jLayout.flow !== 'undefined') {
o.items = [];
element.children().each(function (i) {
if (!$(this).hasClass('ui-resizable-handle')) {
o.items[i] = wrap($(this));
}
});
element.data('jlayout', jLayout.flow(o));
}
if (o.resize) {
elementWrapper.bounds(elementWrapper.preferredSize());
}
elementWrapper.doLayout();
element.css({position: 'relative'});
if ($.ui !== undefined) {
element.addClass('ui-widget');
}
});
};
$.fn.layout.defaults = {
resize: true,
type: 'grid'
};
}(jQuery));
}

View File

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

View File

@ -1,60 +0,0 @@
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

View File

@ -1,23 +0,0 @@
<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>

View File

@ -1,8176 +0,0 @@
/*!
* jQuery JavaScript Library v1.5
* http://jquery.com/
*
* Copyright 2011, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* Includes Sizzle.js
* http://sizzlejs.com/
* Copyright 2011, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
*
* Date: Mon Jan 31 08:31:29 2011 -0500
*/
(function( window, undefined ) {
// Use the correct document accordingly with window argument (sandbox)
var document = window.document;
var jQuery = (function() {
// Define a local copy of jQuery
var jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context, rootjQuery );
},
// Map over jQuery in case of overwrite
_jQuery = window.jQuery,
// Map over the $ in case of overwrite
_$ = window.$,
// A central reference to the root jQuery(document)
rootjQuery,
// A simple way to check for HTML strings or ID strings
// (both of which we optimize for)
quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,
// Check if a string has a non-whitespace character in it
rnotwhite = /\S/,
// Used for trimming whitespace
trimLeft = /^\s+/,
trimRight = /\s+$/,
// Check for digits
rdigit = /\d/,
// Match a standalone tag
rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
// JSON RegExp
rvalidchars = /^[\],:{}\s]*$/,
rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
// Useragent RegExp
rwebkit = /(webkit)[ \/]([\w.]+)/,
ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
rmsie = /(msie) ([\w.]+)/,
rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
// Keep a UserAgent string for use with jQuery.browser
userAgent = navigator.userAgent,
// For matching the engine and version of the browser
browserMatch,
// Has the ready events already been bound?
readyBound = false,
// The deferred used on DOM ready
readyList,
// Promise methods
promiseMethods = "then done fail isResolved isRejected promise".split( " " ),
// The ready event handler
DOMContentLoaded,
// Save a reference to some core methods
toString = Object.prototype.toString,
hasOwn = Object.prototype.hasOwnProperty,
push = Array.prototype.push,
slice = Array.prototype.slice,
trim = String.prototype.trim,
indexOf = Array.prototype.indexOf,
// [[Class]] -> type pairs
class2type = {};
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
init: function( selector, context, rootjQuery ) {
var match, elem, ret, doc;
// Handle $(""), $(null), or $(undefined)
if ( !selector ) {
return this;
}
// Handle $(DOMElement)
if ( selector.nodeType ) {
this.context = this[0] = selector;
this.length = 1;
return this;
}
// The body element only exists once, optimize finding it
if ( selector === "body" && !context && document.body ) {
this.context = document;
this[0] = document.body;
this.selector = "body";
this.length = 1;
return this;
}
// Handle HTML strings
if ( typeof selector === "string" ) {
// Are we dealing with HTML string or an ID?
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] ) {
context = context instanceof jQuery ? context[0] : context;
doc = (context ? context.ownerDocument || context : document);
// If a single string is passed in and it's a single tag
// just do a createElement and skip the rest
ret = rsingleTag.exec( selector );
if ( ret ) {
if ( jQuery.isPlainObject( context ) ) {
selector = [ document.createElement( ret[1] ) ];
jQuery.fn.attr.call( selector, context, true );
} else {
selector = [ doc.createElement( ret[1] ) ];
}
} else {
ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes;
}
return jQuery.merge( this, selector );
// HANDLE: $("#id")
} else {
elem = document.getElementById( match[2] );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
if ( elem && elem.parentNode ) {
// Handle the case where IE and Opera return items
// by name instead of ID
if ( elem.id !== match[2] ) {
return rootjQuery.find( selector );
}
// Otherwise, we inject the element directly into the jQuery object
this.length = 1;
this[0] = elem;
}
this.context = document;
this.selector = selector;
return this;
}
// HANDLE: $(expr, $(...))
} else if ( !context || context.jquery ) {
return (context || rootjQuery).find( selector );
// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {
return this.constructor( context ).find( selector );
}
// HANDLE: $(function)
// Shortcut for document ready
} else if ( jQuery.isFunction( selector ) ) {
return rootjQuery.ready( selector );
}
if (selector.selector !== undefined) {
this.selector = selector.selector;
this.context = selector.context;
}
return jQuery.makeArray( selector, this );
},
// Start with an empty selector
selector: "",
// The current version of jQuery being used
jquery: "1.5",
// The default length of a jQuery object is 0
length: 0,
// The number of elements contained in the matched element set
size: function() {
return this.length;
},
toArray: function() {
return slice.call( this, 0 );
},
// 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 == null ?
// Return a 'clean' array
this.toArray() :
// Return just the object
( num < 0 ? this[ this.length + num ] : 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 = this.constructor();
if ( jQuery.isArray( elems ) ) {
push.apply( ret, elems );
} else {
jQuery.merge( ret, 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;
},
// 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 );
},
ready: function( fn ) {
// Attach the listeners
jQuery.bindReady();
// Add the callback
readyList.done( fn );
return this;
},
eq: function( i ) {
return i === -1 ?
this.slice( i ) :
this.slice( i, +i + 1 );
},
first: function() {
return this.eq( 0 );
},
last: function() {
return this.eq( -1 );
},
slice: function() {
return this.pushStack( slice.apply( this, arguments ),
"slice", slice.call(arguments).join(",") );
},
map: function( callback ) {
return this.pushStack( jQuery.map(this, function( elem, i ) {
return callback.call( elem, i, elem );
}));
},
end: function() {
return this.prevObject || this.constructor(null);
},
// For internal use only.
// Behaves like an Array's method, not like a jQuery method.
push: push,
sort: [].sort,
splice: [].splice
};
// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;
jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false;
// 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 ( name in options ) {
src = target[ name ];
copy = options[ name ];
// Prevent never-ending loop
if ( target === copy ) {
continue;
}
// Recurse if we're merging plain objects or arrays
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
if ( copyIsArray ) {
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : [];
} else {
clone = src && jQuery.isPlainObject(src) ? src : {};
}
// Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy );
// Don't bring in undefined values
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
// Return the modified object
return target;
};
jQuery.extend({
noConflict: function( deep ) {
window.$ = _$;
if ( deep ) {
window.jQuery = _jQuery;
}
return jQuery;
},
// Is the DOM ready to be used? Set to true once it occurs.
isReady: false,
// A counter to track how many items to wait for before
// the ready event fires. See #6781
readyWait: 1,
// Handle when the DOM is ready
ready: function( wait ) {
// A third-party is pushing the ready event forwards
if ( wait === true ) {
jQuery.readyWait--;
}
// Make sure that the DOM is not already loaded
if ( !jQuery.readyWait || (wait !== true && !jQuery.isReady) ) {
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if ( !document.body ) {
return setTimeout( jQuery.ready, 1 );
}
// Remember that the DOM is ready
jQuery.isReady = true;
// If a normal DOM Ready event fired, decrement, and wait if need be
if ( wait !== true && --jQuery.readyWait > 0 ) {
return;
}
// If there are functions bound, to execute
readyList.resolveWith( document, [ jQuery ] );
// Trigger any bound ready events
if ( jQuery.fn.trigger ) {
jQuery( document ).trigger( "ready" ).unbind( "ready" );
}
}
},
bindReady: function() {
if ( readyBound ) {
return;
}
readyBound = true;
// Catch cases where $(document).ready() is called after the
// browser event has already occurred.
if ( document.readyState === "complete" ) {
// Handle it asynchronously to allow scripts the opportunity to delay ready
return setTimeout( jQuery.ready, 1 );
}
// Mozilla, Opera and webkit nightlies currently support this event
if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
// A fallback to window.onload, that will always work
window.addEventListener( "load", 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", DOMContentLoaded);
// A fallback to window.onload, that will always work
window.attachEvent( "onload", jQuery.ready );
// If IE and not a frame
// continually check to see if the document is ready
var toplevel = false;
try {
toplevel = window.frameElement == null;
} catch(e) {}
if ( document.documentElement.doScroll && toplevel ) {
doScrollCheck();
}
}
},
// 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 jQuery.type(obj) === "function";
},
isArray: Array.isArray || function( obj ) {
return jQuery.type(obj) === "array";
},
// A crude way of determining if an object is a window
isWindow: function( obj ) {
return obj && typeof obj === "object" && "setInterval" in obj;
},
isNaN: function( obj ) {
return obj == null || !rdigit.test( obj ) || isNaN( obj );
},
type: function( obj ) {
return obj == null ?
String( obj ) :
class2type[ toString.call(obj) ] || "object";
},
isPlainObject: function( obj ) {
// Must be an Object.
// Because of IE, we also have to check the presence of the constructor property.
// Make sure that DOM nodes and window objects don't pass through, as well
if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
return false;
}
// Not own constructor property must be Object
if ( obj.constructor &&
!hasOwn.call(obj, "constructor") &&
!hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
return false;
}
// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
var key;
for ( key in obj ) {}
return key === undefined || hasOwn.call( obj, key );
},
isEmptyObject: function( obj ) {
for ( var name in obj ) {
return false;
}
return true;
},
error: function( msg ) {
throw msg;
},
parseJSON: function( data ) {
if ( typeof data !== "string" || !data ) {
return null;
}
// Make sure leading/trailing whitespace is removed (IE can't handle it)
data = jQuery.trim( data );
// Make sure the incoming data is actual JSON
// Logic borrowed from http://json.org/json2.js
if ( rvalidchars.test(data.replace(rvalidescape, "@")
.replace(rvalidtokens, "]")
.replace(rvalidbraces, "")) ) {
// Try to use the native JSON parser first
return window.JSON && window.JSON.parse ?
window.JSON.parse( data ) :
(new Function("return " + data))();
} else {
jQuery.error( "Invalid JSON: " + data );
}
},
// Cross-browser xml parsing
// (xml & tmp used internally)
parseXML: function( data , xml , tmp ) {
if ( window.DOMParser ) { // Standard
tmp = new DOMParser();
xml = tmp.parseFromString( data , "text/xml" );
} else { // IE
xml = new ActiveXObject( "Microsoft.XMLDOM" );
xml.async = "false";
xml.loadXML( data );
}
tmp = xml.documentElement;
if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) {
jQuery.error( "Invalid XML: " + data );
}
return xml;
},
noop: function() {},
// Evalulates a script in a global context
globalEval: function( data ) {
if ( data && rnotwhite.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,
isObj = length === undefined || jQuery.isFunction(object);
if ( args ) {
if ( isObj ) {
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 ( isObj ) {
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;
},
// Use native String.trim function wherever possible
trim: trim ?
function( text ) {
return text == null ?
"" :
trim.call( text );
} :
// Otherwise use our own trimming functionality
function( text ) {
return text == null ?
"" :
text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
},
// results is for internal usage only
makeArray: function( array, results ) {
var ret = results || [];
if ( array != null ) {
// The window, strings (and functions) also have 'length'
// The extra typeof function check is to prevent crashes
// in Safari 2 (See: #3039)
// Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
var type = jQuery.type(array);
if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
push.call( ret, array );
} else {
jQuery.merge( ret, array );
}
}
return ret;
},
inArray: function( elem, array ) {
if ( array.indexOf ) {
return array.indexOf( elem );
}
for ( var i = 0, length = array.length; i < length; i++ ) {
if ( array[ i ] === elem ) {
return i;
}
}
return -1;
},
merge: function( first, second ) {
var i = first.length,
j = 0;
if ( typeof second.length === "number" ) {
for ( var l = second.length; j < l; j++ ) {
first[ i++ ] = second[ j ];
}
} else {
while ( second[j] !== undefined ) {
first[ i++ ] = second[ j++ ];
}
}
first.length = i;
return first;
},
grep: function( elems, callback, inv ) {
var ret = [], retVal;
inv = !!inv;
// Go through the array, only saving the items
// that pass the validator function
for ( var i = 0, length = elems.length; i < length; i++ ) {
retVal = !!callback( elems[ i ], i );
if ( inv !== retVal ) {
ret.push( elems[ i ] );
}
}
return ret;
},
// arg is for internal usage only
map: function( elems, callback, arg ) {
var ret = [], value;
// 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++ ) {
value = callback( elems[ i ], i, arg );
if ( value != null ) {
ret[ ret.length ] = value;
}
}
// Flatten any nested arrays
return ret.concat.apply( [], ret );
},
// A global GUID counter for objects
guid: 1,
proxy: function( fn, proxy, thisObject ) {
if ( arguments.length === 2 ) {
if ( typeof proxy === "string" ) {
thisObject = fn;
fn = thisObject[ proxy ];
proxy = undefined;
} else if ( proxy && !jQuery.isFunction( proxy ) ) {
thisObject = proxy;
proxy = undefined;
}
}
if ( !proxy && fn ) {
proxy = function() {
return fn.apply( thisObject || this, arguments );
};
}
// Set the guid of unique handler to the same of original handler, so it can be removed
if ( fn ) {
proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
}
// So proxy can be declared as an argument
return proxy;
},
// Mutifunctional method to get and set values to a collection
// The value/s can be optionally by executed if its a function
access: function( elems, key, value, exec, fn, pass ) {
var length = elems.length;
// Setting many attributes
if ( typeof key === "object" ) {
for ( var k in key ) {
jQuery.access( elems, k, key[k], exec, fn, value );
}
return elems;
}
// Setting one attribute
if ( value !== undefined ) {
// Optionally, function values get executed if exec is true
exec = !pass && exec && jQuery.isFunction(value);
for ( var i = 0; i < length; i++ ) {
fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
}
return elems;
}
// Getting an attribute
return length ? fn( elems[0], key ) : undefined;
},
now: function() {
return (new Date()).getTime();
},
// Create a simple deferred (one callbacks list)
_Deferred: function() {
var // callbacks list
callbacks = [],
// stored [ context , args ]
fired,
// to avoid firing when already doing so
firing,
// flag to know if the deferred has been cancelled
cancelled,
// the deferred itself
deferred = {
// done( f1, f2, ...)
done: function() {
if ( !cancelled ) {
var args = arguments,
i,
length,
elem,
type,
_fired;
if ( fired ) {
_fired = fired;
fired = 0;
}
for ( i = 0, length = args.length; i < length; i++ ) {
elem = args[ i ];
type = jQuery.type( elem );
if ( type === "array" ) {
deferred.done.apply( deferred, elem );
} else if ( type === "function" ) {
callbacks.push( elem );
}
}
if ( _fired ) {
deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
}
}
return this;
},
// resolve with given context and args
resolveWith: function( context, args ) {
if ( !cancelled && !fired && !firing ) {
firing = 1;
try {
while( callbacks[ 0 ] ) {
callbacks.shift().apply( context, args );
}
}
finally {
fired = [ context, args ];
firing = 0;
}
}
return this;
},
// resolve with this as context and given arguments
resolve: function() {
deferred.resolveWith( jQuery.isFunction( this.promise ) ? this.promise() : this, arguments );
return this;
},
// Has this deferred been resolved?
isResolved: function() {
return !!( firing || fired );
},
// Cancel
cancel: function() {
cancelled = 1;
callbacks = [];
return this;
}
};
return deferred;
},
// Full fledged deferred (two callbacks list)
Deferred: function( func ) {
var deferred = jQuery._Deferred(),
failDeferred = jQuery._Deferred(),
promise;
// Add errorDeferred methods, then and promise
jQuery.extend( deferred, {
then: function( doneCallbacks, failCallbacks ) {
deferred.done( doneCallbacks ).fail( failCallbacks );
return this;
},
fail: failDeferred.done,
rejectWith: failDeferred.resolveWith,
reject: failDeferred.resolve,
isRejected: failDeferred.isResolved,
// Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object
promise: function( obj , i /* internal */ ) {
if ( obj == null ) {
if ( promise ) {
return promise;
}
promise = obj = {};
}
i = promiseMethods.length;
while( i-- ) {
obj[ promiseMethods[ i ] ] = deferred[ promiseMethods[ i ] ];
}
return obj;
}
} );
// Make sure only one callback list will be used
deferred.then( failDeferred.cancel, deferred.cancel );
// Unexpose cancel
delete deferred.cancel;
// Call given func if any
if ( func ) {
func.call( deferred, deferred );
}
return deferred;
},
// Deferred helper
when: function( object ) {
var args = arguments,
length = args.length,
deferred = length <= 1 && object && jQuery.isFunction( object.promise ) ?
object :
jQuery.Deferred(),
promise = deferred.promise(),
resolveArray;
if ( length > 1 ) {
resolveArray = new Array( length );
jQuery.each( args, function( index, element ) {
jQuery.when( element ).then( function( value ) {
resolveArray[ index ] = arguments.length > 1 ? slice.call( arguments, 0 ) : value;
if( ! --length ) {
deferred.resolveWith( promise, resolveArray );
}
}, deferred.reject );
} );
} else if ( deferred !== object ) {
deferred.resolve( object );
}
return promise;
},
// Use of jQuery.browser is frowned upon.
// More details: http://docs.jquery.com/Utilities/jQuery.browser
uaMatch: function( ua ) {
ua = ua.toLowerCase();
var match = rwebkit.exec( ua ) ||
ropera.exec( ua ) ||
rmsie.exec( ua ) ||
ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
[];
return { browser: match[1] || "", version: match[2] || "0" };
},
sub: function() {
function jQuerySubclass( selector, context ) {
return new jQuerySubclass.fn.init( selector, context );
}
jQuery.extend( true, jQuerySubclass, this );
jQuerySubclass.superclass = this;
jQuerySubclass.fn = jQuerySubclass.prototype = this();
jQuerySubclass.fn.constructor = jQuerySubclass;
jQuerySubclass.subclass = this.subclass;
jQuerySubclass.fn.init = function init( selector, context ) {
if ( context && context instanceof jQuery && !(context instanceof jQuerySubclass) ) {
context = jQuerySubclass(context);
}
return jQuery.fn.init.call( this, selector, context, rootjQuerySubclass );
};
jQuerySubclass.fn.init.prototype = jQuerySubclass.fn;
var rootjQuerySubclass = jQuerySubclass(document);
return jQuerySubclass;
},
browser: {}
});
// Create readyList deferred
readyList = jQuery._Deferred();
// Populate the class2type map
jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
class2type[ "[object " + name + "]" ] = name.toLowerCase();
});
browserMatch = jQuery.uaMatch( userAgent );
if ( browserMatch.browser ) {
jQuery.browser[ browserMatch.browser ] = true;
jQuery.browser.version = browserMatch.version;
}
// Deprecated, use jQuery.browser.webkit instead
if ( jQuery.browser.webkit ) {
jQuery.browser.safari = true;
}
if ( indexOf ) {
jQuery.inArray = function( elem, array ) {
return indexOf.call( array, elem );
};
}
// IE doesn't match non-breaking spaces with \s
if ( rnotwhite.test( "\xA0" ) ) {
trimLeft = /^[\s\xA0]+/;
trimRight = /[\s\xA0]+$/;
}
// All jQuery objects should point back to these
rootjQuery = jQuery(document);
// Cleanup functions for the document ready method
if ( document.addEventListener ) {
DOMContentLoaded = function() {
document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
jQuery.ready();
};
} else if ( document.attachEvent ) {
DOMContentLoaded = function() {
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if ( document.readyState === "complete" ) {
document.detachEvent( "onreadystatechange", DOMContentLoaded );
jQuery.ready();
}
};
}
// The DOM ready check for Internet Explorer
function doScrollCheck() {
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(e) {
setTimeout( doScrollCheck, 1 );
return;
}
// and execute any waiting functions
jQuery.ready();
}
// Expose jQuery to the global object
return (window.jQuery = window.$ = jQuery);
})();
(function() {
jQuery.support = {};
var div = document.createElement("div");
div.style.display = "none";
div.innerHTML = " <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
var all = div.getElementsByTagName("*"),
a = div.getElementsByTagName("a")[0],
select = document.createElement("select"),
opt = select.appendChild( document.createElement("option") );
// 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 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)
// Use a regex to work around a WebKit issue. See #5145
opacity: /^0.55$/.test( a.style.opacity ),
// Verify style float existence
// (IE uses styleFloat instead of cssFloat)
cssFloat: !!a.style.cssFloat,
// Make sure that if no value is specified for a checkbox
// that it defaults to "on".
// (WebKit defaults to "" instead)
checkOn: div.getElementsByTagName("input")[0].value === "on",
// Make sure that a selected-by-default option has a working selected property.
// (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
optSelected: opt.selected,
// Will be defined later
deleteExpando: true,
optDisabled: false,
checkClone: false,
_scriptEval: null,
noCloneEvent: true,
boxModel: null,
inlineBlockNeedsLayout: false,
shrinkWrapBlocks: false,
reliableHiddenOffsets: true
};
// Make sure that the options inside disabled selects aren't marked as disabled
// (WebKit marks them as diabled)
select.disabled = true;
jQuery.support.optDisabled = !opt.disabled;
jQuery.support.scriptEval = function() {
if ( jQuery.support._scriptEval === null ) {
var root = document.documentElement,
script = document.createElement("script"),
id = "script" + jQuery.now();
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 ];
} else {
jQuery.support._scriptEval = false;
}
root.removeChild( script );
// release memory in IE
root = script = id = null;
}
return jQuery.support._scriptEval;
};
// Test to see if it's possible to delete an expando from an element
// Fails in Internet Explorer
try {
delete div.test;
} catch(e) {
jQuery.support.deleteExpando = false;
}
if ( div.attachEvent && div.fireEvent ) {
div.attachEvent("onclick", function click() {
// Cloning a node shouldn't copy over any
// bound event handlers (IE does this)
jQuery.support.noCloneEvent = false;
div.detachEvent("onclick", click);
});
div.cloneNode(true).fireEvent("onclick");
}
div = document.createElement("div");
div.innerHTML = "<input type='radio' name='radiotest' checked='checked'/>";
var fragment = document.createDocumentFragment();
fragment.appendChild( div.firstChild );
// WebKit doesn't clone checked state correctly in fragments
jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked;
// 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"),
body = document.getElementsByTagName("body")[0];
// Frameset documents with no body should not run this code
if ( !body ) {
return;
}
div.style.width = div.style.paddingLeft = "1px";
body.appendChild( div );
jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
if ( "zoom" in div.style ) {
// Check if natively block-level elements act like inline-block
// elements when setting their display to 'inline' and giving
// them layout
// (IE < 8 does this)
div.style.display = "inline";
div.style.zoom = 1;
jQuery.support.inlineBlockNeedsLayout = div.offsetWidth === 2;
// Check if elements with layout shrink-wrap their children
// (IE 6 does this)
div.style.display = "";
div.innerHTML = "<div style='width:4px;'></div>";
jQuery.support.shrinkWrapBlocks = div.offsetWidth !== 2;
}
div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
var tds = div.getElementsByTagName("td");
// Check if table cells still have offsetWidth/Height when they are set
// to display:none and there are still other visible table cells in a
// table row; if so, offsetWidth/Height are not reliable for use when
// determining if an element has been hidden directly using
// display:none (it is still safe to use offsets if a parent element is
// hidden; don safety goggles and see bug #4512 for more information).
// (only IE 8 fails this test)
jQuery.support.reliableHiddenOffsets = tds[0].offsetHeight === 0;
tds[0].style.display = "";
tds[1].style.display = "none";
// Check if empty table cells still have offsetWidth/Height
// (IE < 8 fail this test)
jQuery.support.reliableHiddenOffsets = jQuery.support.reliableHiddenOffsets && tds[0].offsetHeight === 0;
div.innerHTML = "";
body.removeChild( div ).style.display = "none";
div = tds = null;
});
// Technique from Juriy Zaytsev
// http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
var eventSupported = function( eventName ) {
var el = document.createElement("div");
eventName = "on" + eventName;
// We only care about the case where non-standard event systems
// are used, namely in IE. Short-circuiting here helps us to
// avoid an eval call (in setAttribute) which can cause CSP
// to go haywire. See: https://developer.mozilla.org/en/Security/CSP
if ( !el.attachEvent ) {
return true;
}
var isSupported = (eventName in el);
if ( !isSupported ) {
el.setAttribute(eventName, "return;");
isSupported = typeof el[eventName] === "function";
}
el = null;
return isSupported;
};
jQuery.support.submitBubbles = eventSupported("submit");
jQuery.support.changeBubbles = eventSupported("change");
// release memory in IE
div = all = a = null;
})();
var rbrace = /^(?:\{.*\}|\[.*\])$/;
jQuery.extend({
cache: {},
// Please use with caution
uuid: 0,
// Unique for each copy of jQuery on the page
// Non-digits removed to match rinlinejQuery
expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
// The following elements throw uncatchable exceptions if you
// attempt to add expando properties to them.
noData: {
"embed": true,
// Ban all objects except for Flash (which handle expandos)
"object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
"applet": true
},
hasData: function( elem ) {
elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
return !!elem && !jQuery.isEmptyObject(elem);
},
data: function( elem, name, data, pvt /* Internal Use Only */ ) {
if ( !jQuery.acceptData( elem ) ) {
return;
}
var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache,
// We have to handle DOM nodes and JS objects differently because IE6-7
// can't GC object references properly across the DOM-JS boundary
isNode = elem.nodeType,
// Only DOM nodes need the global jQuery cache; JS object data is
// attached directly to the object so GC can occur automatically
cache = isNode ? jQuery.cache : elem,
// Only defining an ID for JS objects if its cache already exists allows
// the code to shortcut on the same path as a DOM node with no cache
id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando;
// Avoid doing any more work than we need to when trying to get data on an
// object that has no data at all
if ( (!id || (pvt && id && !cache[ id ][ internalKey ])) && getByName && data === undefined ) {
return;
}
if ( !id ) {
// Only DOM nodes need a new unique ID for each element since their data
// ends up in the global cache
if ( isNode ) {
elem[ jQuery.expando ] = id = ++jQuery.uuid;
} else {
id = jQuery.expando;
}
}
if ( !cache[ id ] ) {
cache[ id ] = {};
}
// An object can be passed to jQuery.data instead of a key/value pair; this gets
// shallow copied over onto the existing cache
if ( typeof name === "object" ) {
if ( pvt ) {
cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name);
} else {
cache[ id ] = jQuery.extend(cache[ id ], name);
}
}
thisCache = cache[ id ];
// Internal jQuery data is stored in a separate object inside the object's data
// cache in order to avoid key collisions between internal data and user-defined
// data
if ( pvt ) {
if ( !thisCache[ internalKey ] ) {
thisCache[ internalKey ] = {};
}
thisCache = thisCache[ internalKey ];
}
if ( data !== undefined ) {
thisCache[ name ] = data;
}
// TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should
// not attempt to inspect the internal events object using jQuery.data, as this
// internal data object is undocumented and subject to change.
if ( name === "events" && !thisCache[name] ) {
return thisCache[ internalKey ] && thisCache[ internalKey ].events;
}
return getByName ? thisCache[ name ] : thisCache;
},
removeData: function( elem, name, pvt /* Internal Use Only */ ) {
if ( !jQuery.acceptData( elem ) ) {
return;
}
var internalKey = jQuery.expando, isNode = elem.nodeType,
// See jQuery.data for more information
cache = isNode ? jQuery.cache : elem,
// See jQuery.data for more information
id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
// If there is already no cache entry for this object, there is no
// purpose in continuing
if ( !cache[ id ] ) {
return;
}
if ( name ) {
var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ];
if ( thisCache ) {
delete thisCache[ name ];
// If there is no data left in the cache, we want to continue
// and let the cache object itself get destroyed
if ( !jQuery.isEmptyObject(thisCache) ) {
return;
}
}
}
// See jQuery.data for more information
if ( pvt ) {
delete cache[ id ][ internalKey ];
// Don't destroy the parent cache unless the internal data object
// had been the only thing left in it
if ( !jQuery.isEmptyObject(cache[ id ]) ) {
return;
}
}
var internalCache = cache[ id ][ internalKey ];
// Browsers that fail expando deletion also refuse to delete expandos on
// the window, but it will allow it on all other JS objects; other browsers
// don't care
if ( jQuery.support.deleteExpando || cache != window ) {
delete cache[ id ];
} else {
cache[ id ] = null;
}
// We destroyed the entire user cache at once because it's faster than
// iterating through each key, but we need to continue to persist internal
// data if it existed
if ( internalCache ) {
cache[ id ] = {};
cache[ id ][ internalKey ] = internalCache;
// Otherwise, we need to eliminate the expando on the node to avoid
// false lookups in the cache for entries that no longer exist
} else if ( isNode ) {
// IE does not allow us to delete expando properties from nodes,
// nor does it have a removeAttribute function on Document nodes;
// we must handle all of these cases
if ( jQuery.support.deleteExpando ) {
delete elem[ jQuery.expando ];
} else if ( elem.removeAttribute ) {
elem.removeAttribute( jQuery.expando );
} else {
elem[ jQuery.expando ] = null;
}
}
},
// For internal use only.
_data: function( elem, name, data ) {
return jQuery.data( elem, name, data, true );
},
// A method for determining if a DOM node can handle the data expando
acceptData: function( elem ) {
if ( elem.nodeName ) {
var match = jQuery.noData[ elem.nodeName.toLowerCase() ];
if ( match ) {
return !(match === true || elem.getAttribute("classid") !== match);
}
}
return true;
}
});
jQuery.fn.extend({
data: function( key, value ) {
var data = null;
if ( typeof key === "undefined" ) {
if ( this.length ) {
data = jQuery.data( this[0] );
if ( this[0].nodeType === 1 ) {
var attr = this[0].attributes, name;
for ( var i = 0, l = attr.length; i < l; i++ ) {
name = attr[i].name;
if ( name.indexOf( "data-" ) === 0 ) {
name = name.substr( 5 );
dataAttr( this[0], name, data[ name ] );
}
}
}
}
return data;
} else if ( typeof key === "object" ) {
return this.each(function() {
jQuery.data( this, key );
});
}
var parts = key.split(".");
parts[1] = parts[1] ? "." + parts[1] : "";
if ( value === undefined ) {
data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
// Try to fetch any internally stored data first
if ( data === undefined && this.length ) {
data = jQuery.data( this[0], key );
data = dataAttr( this[0], key, data );
}
return data === undefined && parts[1] ?
this.data( parts[0] ) :
data;
} else {
return this.each(function() {
var $this = jQuery( this ),
args = [ parts[0], value ];
$this.triggerHandler( "setData" + parts[1] + "!", args );
jQuery.data( this, key, value );
$this.triggerHandler( "changeData" + parts[1] + "!", args );
});
}
},
removeData: function( key ) {
return this.each(function() {
jQuery.removeData( this, key );
});
}
});
function dataAttr( elem, key, data ) {
// If nothing was found internally, try to fetch any
// data from the HTML5 data-* attribute
if ( data === undefined && elem.nodeType === 1 ) {
data = elem.getAttribute( "data-" + key );
if ( typeof data === "string" ) {
try {
data = data === "true" ? true :
data === "false" ? false :
data === "null" ? null :
!jQuery.isNaN( data ) ? parseFloat( data ) :
rbrace.test( data ) ? jQuery.parseJSON( data ) :
data;
} catch( e ) {}
// Make sure we set the data so it isn't changed later
jQuery.data( elem, key, data );
} else {
data = undefined;
}
}
return data;
}
jQuery.extend({
queue: function( elem, type, data ) {
if ( !elem ) {
return;
}
type = (type || "fx") + "queue";
var q = jQuery._data( elem, type );
// Speed up dequeue by getting out quickly if this is just a lookup
if ( !data ) {
return q || [];
}
if ( !q || jQuery.isArray(data) ) {
q = jQuery._data( elem, type, jQuery.makeArray(data) );
} else {
q.push( data );
}
return q;
},
dequeue: function( elem, type ) {
type = type || "fx";
var queue = jQuery.queue( elem, type ),
fn = queue.shift();
// If the fx queue is dequeued, always remove the progress sentinel
if ( fn === "inprogress" ) {
fn = queue.shift();
}
if ( fn ) {
// Add a progress sentinel to prevent the fx queue from being
// automatically dequeued
if ( type === "fx" ) {
queue.unshift("inprogress");
}
fn.call(elem, function() {
jQuery.dequeue(elem, type);
});
}
if ( !queue.length ) {
jQuery.removeData( elem, type + "queue", true );
}
}
});
jQuery.fn.extend({
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( i ) {
var queue = jQuery.queue( this, type, data );
if ( type === "fx" && queue[0] !== "inprogress" ) {
jQuery.dequeue( this, type );
}
});
},
dequeue: function( type ) {
return this.each(function() {
jQuery.dequeue( this, type );
});
},
// Based off of the plugin by Clint Helfers, with permission.
// http://blindsignals.com/index.php/2009/07/jquery-delay/
delay: function( time, type ) {
time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
type = type || "fx";
return this.queue( type, function() {
var elem = this;
setTimeout(function() {
jQuery.dequeue( elem, type );
}, time );
});
},
clearQueue: function( type ) {
return this.queue( type || "fx", [] );
}
});
var rclass = /[\n\t\r]/g,
rspaces = /\s+/,
rreturn = /\r/g,
rspecialurl = /^(?:href|src|style)$/,
rtype = /^(?:button|input)$/i,
rfocusable = /^(?:button|input|object|select|textarea)$/i,
rclickable = /^a(?:rea)?$/i,
rradiocheck = /^(?:radio|checkbox)$/i;
jQuery.props = {
"for": "htmlFor",
"class": "className",
readonly: "readOnly",
maxlength: "maxLength",
cellspacing: "cellSpacing",
rowspan: "rowSpan",
colspan: "colSpan",
tabindex: "tabIndex",
usemap: "useMap",
frameborder: "frameBorder"
};
jQuery.fn.extend({
attr: function( name, value ) {
return jQuery.access( this, name, value, true, jQuery.attr );
},
removeAttr: function( name, fn ) {
return this.each(function(){
jQuery.attr( this, name, "" );
if ( this.nodeType === 1 ) {
this.removeAttribute( name );
}
});
},
addClass: function( value ) {
if ( jQuery.isFunction(value) ) {
return this.each(function(i) {
var self = jQuery(this);
self.addClass( value.call(this, i, self.attr("class")) );
});
}
if ( value && typeof value === "string" ) {
var classNames = (value || "").split( rspaces );
for ( var i = 0, l = this.length; i < l; i++ ) {
var elem = this[i];
if ( elem.nodeType === 1 ) {
if ( !elem.className ) {
elem.className = value;
} else {
var className = " " + elem.className + " ",
setClass = elem.className;
for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {
setClass += " " + classNames[c];
}
}
elem.className = jQuery.trim( setClass );
}
}
}
}
return this;
},
removeClass: function( value ) {
if ( jQuery.isFunction(value) ) {
return this.each(function(i) {
var self = jQuery(this);
self.removeClass( value.call(this, i, self.attr("class")) );
});
}
if ( (value && typeof value === "string") || value === undefined ) {
var classNames = (value || "").split( rspaces );
for ( var i = 0, l = this.length; i < l; i++ ) {
var elem = this[i];
if ( elem.nodeType === 1 && elem.className ) {
if ( value ) {
var className = (" " + elem.className + " ").replace(rclass, " ");
for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
className = className.replace(" " + classNames[c] + " ", " ");
}
elem.className = jQuery.trim( className );
} else {
elem.className = "";
}
}
}
}
return this;
},
toggleClass: function( value, stateVal ) {
var type = typeof value,
isBool = typeof stateVal === "boolean";
if ( jQuery.isFunction( value ) ) {
return this.each(function(i) {
var self = jQuery(this);
self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal );
});
}
return this.each(function() {
if ( type === "string" ) {
// toggle individual class names
var className,
i = 0,
self = jQuery( this ),
state = stateVal,
classNames = value.split( rspaces );
while ( (className = classNames[ i++ ]) ) {
// check each className given, space seperated list
state = isBool ? state : !self.hasClass( className );
self[ state ? "addClass" : "removeClass" ]( className );
}
} else if ( type === "undefined" || type === "boolean" ) {
if ( this.className ) {
// store className if set
jQuery._data( this, "__className__", this.className );
}
// toggle whole className
this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
}
});
},
hasClass: function( selector ) {
var className = " " + selector + " ";
for ( var i = 0, l = this.length; i < l; i++ ) {
if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
return true;
}
}
return false;
},
val: function( value ) {
if ( !arguments.length ) {
var elem = this[0];
if ( elem ) {
if ( jQuery.nodeName( elem, "option" ) ) {
// attributes.value is undefined in Blackberry 4.7 but
// uses .value. See #6932
var val = elem.attributes.value;
return !val || val.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 ];
// Don't return options that are disabled or in a disabled optgroup
if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
(!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
// Get the specific 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;
}
// Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) {
return elem.getAttribute("value") === null ? "on" : elem.value;
}
// Everything else, we just grab the value
return (elem.value || "").replace(rreturn, "");
}
return undefined;
}
var isFunction = jQuery.isFunction(value);
return this.each(function(i) {
var self = jQuery(this), val = value;
if ( this.nodeType !== 1 ) {
return;
}
if ( isFunction ) {
val = value.call(this, i, self.val());
}
// Treat null/undefined as ""; convert numbers to string
if ( val == null ) {
val = "";
} else if ( typeof val === "number" ) {
val += "";
} else if ( jQuery.isArray(val) ) {
val = jQuery.map(val, function (value) {
return value == null ? "" : value + "";
});
}
if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) {
this.checked = jQuery.inArray( self.val(), val ) >= 0;
} else if ( jQuery.nodeName( this, "select" ) ) {
var values = jQuery.makeArray(val);
jQuery( "option", this ).each(function() {
this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
});
if ( !values.length ) {
this.selectedIndex = -1;
}
} else {
this.value = val;
}
});
}
});
jQuery.extend({
attrFn: {
val: true,
css: true,
html: true,
text: true,
data: true,
width: true,
height: true,
offset: true
},
attr: function( elem, name, value, pass ) {
// don't get/set attributes on text, comment and attribute nodes
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || elem.nodeType === 2 ) {
return undefined;
}
if ( pass && name in jQuery.attrFn ) {
return jQuery(elem)[name](value);
}
var notxml = elem.nodeType !== 1 || !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)
if ( elem.nodeType === 1 ) {
// These attributes require special treatment
var special = rspecialurl.test( name );
// Safari mis-reports the default selected property of an option
// Accessing the parent's selectedIndex property fixes it
if ( name === "selected" && !jQuery.support.optSelected ) {
var parent = elem.parentNode;
if ( parent ) {
parent.selectedIndex;
// Make sure that it also works with optgroups, see #5701
if ( parent.parentNode ) {
parent.parentNode.selectedIndex;
}
}
}
// If applicable, access the attribute via the DOM 0 way
// 'in' checks fail in Blackberry 4.7 #6931
if ( (name in elem || elem[ name ] !== undefined) && notxml && !special ) {
if ( set ) {
// We can't allow the type property to be changed (since it causes problems in IE)
if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) {
jQuery.error( "type property can't be changed" );
}
if ( value === null ) {
if ( elem.nodeType === 1 ) {
elem.removeAttribute( name );
}
} else {
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 :
rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
0 :
undefined;
}
return elem[ name ];
}
if ( !jQuery.support.style && notxml && name === "style" ) {
if ( set ) {
elem.style.cssText = "" + value;
}
return elem.style.cssText;
}
if ( set ) {
// convert the value to a string (all browsers do this but IE) see #1070
elem.setAttribute( name, "" + value );
}
// Ensure that missing attributes return undefined
// Blackberry 4.7 returns "" from getAttribute #6938
if ( !elem.attributes[ name ] && (elem.hasAttribute && !elem.hasAttribute( name )) ) {
return undefined;
}
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;
}
// Handle everything which isn't a DOM element node
if ( set ) {
elem[ name ] = value;
}
return elem[ name ];
}
});
var rnamespaces = /\.(.*)$/,
rformElems = /^(?:textarea|input|select)$/i,
rperiod = /\./g,
rspace = / /g,
rescape = /[^\w\s.|`]/g,
fcleanup = function( nm ) {
return nm.replace(rescape, "\\$&");
},
eventKey = "events";
/*
* 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 ( jQuery.isWindow( elem ) && ( elem !== window && !elem.frameElement ) ) {
elem = window;
}
if ( handler === false ) {
handler = returnFalse;
} else if ( !handler ) {
// Fixes bug #7229. Fix recommended by jdalton
return;
}
var handleObjIn, handleObj;
if ( handler.handler ) {
handleObjIn = handler;
handler = handleObjIn.handler;
}
// Make sure that the function being executed has a unique ID
if ( !handler.guid ) {
handler.guid = jQuery.guid++;
}
// Init the element's event structure
var elemData = jQuery._data( elem );
// If no elemData is found then we must be trying to bind to one of the
// banned noData elements
if ( !elemData ) {
return;
}
var events = elemData[ eventKey ],
eventHandle = elemData.handle;
if ( typeof events === "function" ) {
// On plain objects events is a fn that holds the the data
// which prevents this data from being JSON serialized
// the function does not need to be called, it just contains the data
eventHandle = events.handle;
events = events.events;
} else if ( !events ) {
if ( !elem.nodeType ) {
// On plain objects, create a fn that acts as the holder
// of the values to avoid JSON serialization of event data
elemData[ eventKey ] = elemData = function(){};
}
elemData.events = events = {};
}
if ( !eventHandle ) {
elemData.handle = eventHandle = 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( eventHandle.elem, arguments ) :
undefined;
};
}
// Add elem as a property of the handle function
// This is to prevent a memory leak with non-native events in IE.
eventHandle.elem = elem;
// Handle multiple events separated by a space
// jQuery(...).bind("mouseover mouseout", fn);
types = types.split(" ");
var type, i = 0, namespaces;
while ( (type = types[ i++ ]) ) {
handleObj = handleObjIn ?
jQuery.extend({}, handleObjIn) :
{ handler: handler, data: data };
// Namespaced event handlers
if ( type.indexOf(".") > -1 ) {
namespaces = type.split(".");
type = namespaces.shift();
handleObj.namespace = namespaces.slice(0).sort().join(".");
} else {
namespaces = [];
handleObj.namespace = "";
}
handleObj.type = type;
if ( !handleObj.guid ) {
handleObj.guid = handler.guid;
}
// Get the current list of functions bound to this event
var handlers = events[ type ],
special = jQuery.event.special[ type ] || {};
// 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 ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
// Bind the global event handler to the element
if ( elem.addEventListener ) {
elem.addEventListener( type, eventHandle, false );
} else if ( elem.attachEvent ) {
elem.attachEvent( "on" + type, eventHandle );
}
}
}
if ( special.add ) {
special.add.call( elem, handleObj );
if ( !handleObj.handler.guid ) {
handleObj.handler.guid = handler.guid;
}
}
// Add the function to the element's handler list
handlers.push( handleObj );
// 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;
},
global: {},
// Detach an event or set of events from an element
remove: function( elem, types, handler, pos ) {
// don't do events on text and comment nodes
if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
return;
}
if ( handler === false ) {
handler = returnFalse;
}
var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
events = elemData && elemData[ eventKey ];
if ( !elemData || !events ) {
return;
}
if ( typeof events === "function" ) {
elemData = events;
events = events.events;
}
// types is actually an event object here
if ( types && types.type ) {
handler = types.handler;
types = types.type;
}
// Unbind all events for the element
if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
types = types || "";
for ( type in events ) {
jQuery.event.remove( elem, type + types );
}
return;
}
// Handle multiple events separated by a space
// jQuery(...).unbind("mouseover mouseout", fn);
types = types.split(" ");
while ( (type = types[ i++ ]) ) {
origType = type;
handleObj = null;
all = type.indexOf(".") < 0;
namespaces = [];
if ( !all ) {
// Namespaced event handlers
namespaces = type.split(".");
type = namespaces.shift();
namespace = new RegExp("(^|\\.)" +
jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)");
}
eventType = events[ type ];
if ( !eventType ) {
continue;
}
if ( !handler ) {
for ( j = 0; j < eventType.length; j++ ) {
handleObj = eventType[ j ];
if ( all || namespace.test( handleObj.namespace ) ) {
jQuery.event.remove( elem, origType, handleObj.handler, j );
eventType.splice( j--, 1 );
}
}
continue;
}
special = jQuery.event.special[ type ] || {};
for ( j = pos || 0; j < eventType.length; j++ ) {
handleObj = eventType[ j ];
if ( handler.guid === handleObj.guid ) {
// remove the given handler for the given type
if ( all || namespace.test( handleObj.namespace ) ) {
if ( pos == null ) {
eventType.splice( j--, 1 );
}
if ( special.remove ) {
special.remove.call( elem, handleObj );
}
}
if ( pos != null ) {
break;
}
}
}
// remove generic event handler if no more handlers exist
if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
jQuery.removeEvent( elem, type, elemData.handle );
}
ret = null;
delete events[ type ];
}
}
// Remove the expando if it's no longer used
if ( jQuery.isEmptyObject( events ) ) {
var handle = elemData.handle;
if ( handle ) {
handle.elem = null;
}
delete elemData.events;
delete elemData.handle;
if ( typeof elemData === "function" ) {
jQuery.removeData( elem, eventKey, true );
} else if ( jQuery.isEmptyObject( elemData ) ) {
jQuery.removeData( elem, undefined, true );
}
}
},
// bubbling is internal
trigger: function( event, data, elem /*, bubbling */ ) {
// Event object or event type
var type = event.type || event,
bubbling = arguments[3];
if ( !bubbling ) {
event = typeof event === "object" ?
// jQuery.Event object
event[ jQuery.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 ( jQuery.event.global[ type ] ) {
// XXX This code smells terrible. event.js should not be directly
// inspecting the data cache
jQuery.each( jQuery.cache, function() {
// internalKey variable is just used to make it easier to find
// and potentially change this stuff later; currently it just
// points to jQuery.expando
var internalKey = jQuery.expando,
internalCache = this[ internalKey ];
if ( internalCache && internalCache.events && internalCache.events[type] ) {
jQuery.event.trigger( event, data, internalCache.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 = elem.nodeType ?
jQuery._data( elem, "handle" ) :
(jQuery._data( elem, eventKey ) || {}).handle;
if ( handle ) {
handle.apply( elem, data );
}
var parent = elem.parentNode || elem.ownerDocument;
// Trigger an inline bound script
try {
if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
event.result = false;
event.preventDefault();
}
}
// prevent IE from throwing an error for some elements with some event types, see #3533
} catch (inlineError) {}
if ( !event.isPropagationStopped() && parent ) {
jQuery.event.trigger( event, data, parent, true );
} else if ( !event.isDefaultPrevented() ) {
var old,
target = event.target,
targetType = type.replace( rnamespaces, "" ),
isClick = jQuery.nodeName( target, "a" ) && targetType === "click",
special = jQuery.event.special[ targetType ] || {};
if ( (!special._default || special._default.call( elem, event ) === false) &&
!isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
try {
if ( target[ targetType ] ) {
// Make sure that we don't accidentally re-trigger the onFOO events
old = target[ "on" + targetType ];
if ( old ) {
target[ "on" + targetType ] = null;
}
jQuery.event.triggered = true;
target[ targetType ]();
}
// prevent IE from throwing an error for some elements with some event types, see #3533
} catch (triggerError) {}
if ( old ) {
target[ "on" + targetType ] = old;
}
jQuery.event.triggered = false;
}
}
},
handle: function( event ) {
var all, handlers, namespaces, namespace_re, events,
namespace_sort = [],
args = jQuery.makeArray( arguments );
event = args[0] = jQuery.event.fix( event || window.event );
event.currentTarget = this;
// Namespaced event handlers
all = event.type.indexOf(".") < 0 && !event.exclusive;
if ( !all ) {
namespaces = event.type.split(".");
event.type = namespaces.shift();
namespace_sort = namespaces.slice(0).sort();
namespace_re = new RegExp("(^|\\.)" + namespace_sort.join("\\.(?:.*\\.)?") + "(\\.|$)");
}
event.namespace = event.namespace || namespace_sort.join(".");
events = jQuery._data(this, eventKey);
if ( typeof events === "function" ) {
events = events.events;
}
handlers = (events || {})[ event.type ];
if ( events && handlers ) {
// Clone the handlers to prevent manipulation
handlers = handlers.slice(0);
for ( var j = 0, l = handlers.length; j < l; j++ ) {
var handleObj = handlers[ j ];
// Filter the functions by class
if ( all || namespace_re.test( handleObj.namespace ) ) {
// Pass in a reference to the handler function itself
// So that we can later remove it
event.handler = handleObj.handler;
event.data = handleObj.data;
event.handleObj = handleObj;
var ret = handleObj.handler.apply( this, args );
if ( ret !== undefined ) {
event.result = ret;
if ( ret === false ) {
event.preventDefault();
event.stopPropagation();
}
}
if ( event.isImmediatePropagationStopped() ) {
break;
}
}
}
}
return event.result;
},
props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
fix: function( event ) {
if ( event[ jQuery.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 ) {
// Fixes #1925 where srcElement might not be defined either
event.target = event.srcElement || document;
}
// 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 && doc.clientLeft || body && body.clientLeft || 0);
event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
}
// Add which for key events
if ( event.which == null && (event.charCode != null || event.keyCode != null) ) {
event.which = event.charCode != null ? 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 !== undefined ) {
event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
}
return event;
},
// Deprecated, use jQuery.guid instead
guid: 1E8,
// Deprecated, use jQuery.proxy instead
proxy: jQuery.proxy,
special: {
ready: {
// Make sure the ready event is setup
setup: jQuery.bindReady,
teardown: jQuery.noop
},
live: {
add: function( handleObj ) {
jQuery.event.add( this,
liveConvert( handleObj.origType, handleObj.selector ),
jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) );
},
remove: function( handleObj ) {
jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj );
}
},
beforeunload: {
setup: function( data, namespaces, eventHandle ) {
// We only want to do this special case on windows
if ( jQuery.isWindow( this ) ) {
this.onbeforeunload = eventHandle;
}
},
teardown: function( namespaces, eventHandle ) {
if ( this.onbeforeunload === eventHandle ) {
this.onbeforeunload = null;
}
}
}
}
};
jQuery.removeEvent = document.removeEventListener ?
function( elem, type, handle ) {
if ( elem.removeEventListener ) {
elem.removeEventListener( type, handle, false );
}
} :
function( elem, type, handle ) {
if ( elem.detachEvent ) {
elem.detachEvent( "on" + type, handle );
}
};
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;
// Events bubbling up the document may have been marked as prevented
// by a handler lower down the tree; reflect the correct value.
this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false ||
src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse;
// 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 = jQuery.now();
// Mark it as fixed
this[ jQuery.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)
} else {
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;
// Firefox sometimes assigns relatedTarget a XUL element
// which we cannot access the parentNode property of
try {
// Traverse up the tree
while ( parent && parent !== this ) {
parent = parent.parentNode;
}
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 );
}
// assuming we've left the element since we most likely mousedover a xul element
} catch(e) { }
},
// In case of event delegation, we only need to rename the event.type,
// liveHandler will take care of the rest.
delegate = function( event ) {
event.type = event.data;
jQuery.event.handle.apply( this, arguments );
};
// Create mouseenter and mouseleave events
jQuery.each({
mouseenter: "mouseover",
mouseleave: "mouseout"
}, function( orig, fix ) {
jQuery.event.special[ orig ] = {
setup: function( data ) {
jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
},
teardown: function( data ) {
jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
}
};
});
// submit delegation
if ( !jQuery.support.submitBubbles ) {
jQuery.event.special.submit = {
setup: function( data, namespaces ) {
if ( this.nodeName && this.nodeName.toLowerCase() !== "form" ) {
jQuery.event.add(this, "click.specialSubmit", function( e ) {
var elem = e.target,
type = elem.type;
if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
e.liveFired = undefined;
return trigger( "submit", this, arguments );
}
});
jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
var elem = e.target,
type = elem.type;
if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
e.liveFired = undefined;
return trigger( "submit", this, arguments );
}
});
} else {
return false;
}
},
teardown: function( namespaces ) {
jQuery.event.remove( this, ".specialSubmit" );
}
};
}
// change delegation, happens here so we have bind.
if ( !jQuery.support.changeBubbles ) {
var changeFilters,
getVal = function( elem ) {
var type = elem.type, val = elem.value;
if ( type === "radio" || type === "checkbox" ) {
val = elem.checked;
} else if ( type === "select-multiple" ) {
val = elem.selectedIndex > -1 ?
jQuery.map( elem.options, function( elem ) {
return elem.selected;
}).join("-") :
"";
} else if ( elem.nodeName.toLowerCase() === "select" ) {
val = elem.selectedIndex;
}
return val;
},
testChange = function testChange( e ) {
var elem = e.target, data, val;
if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) {
return;
}
data = jQuery._data( elem, "_change_data" );
val = getVal(elem);
// the current data will be also retrieved by beforeactivate
if ( e.type !== "focusout" || elem.type !== "radio" ) {
jQuery._data( elem, "_change_data", val );
}
if ( data === undefined || val === data ) {
return;
}
if ( data != null || val ) {
e.type = "change";
e.liveFired = undefined;
return jQuery.event.trigger( e, arguments[1], elem );
}
};
jQuery.event.special.change = {
filters: {
focusout: testChange,
beforedeactivate: testChange,
click: function( e ) {
var elem = e.target, type = elem.type;
if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
return testChange.call( this, e );
}
},
// Change has to be called before submit
// Keydown will be called before keypress, which is used in submit-event delegation
keydown: function( e ) {
var elem = e.target, type = elem.type;
if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
(e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
type === "select-multiple" ) {
return testChange.call( this, e );
}
},
// Beforeactivate happens also before the previous element is blurred
// with this event you can't trigger a change event, but you can store
// information
beforeactivate: function( e ) {
var elem = e.target;
jQuery._data( elem, "_change_data", getVal(elem) );
}
},
setup: function( data, namespaces ) {
if ( this.type === "file" ) {
return false;
}
for ( var type in changeFilters ) {
jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
}
return rformElems.test( this.nodeName );
},
teardown: function( namespaces ) {
jQuery.event.remove( this, ".specialChange" );
return rformElems.test( this.nodeName );
}
};
changeFilters = jQuery.event.special.change.filters;
// Handle when the input is .focus()'d
changeFilters.focus = changeFilters.beforeactivate;
}
function trigger( type, elem, args ) {
args[0].type = type;
return jQuery.event.handle.apply( elem, args );
}
// Create "bubbling" focus and blur events
if ( document.addEventListener ) {
jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
jQuery.event.special[ fix ] = {
setup: function() {
this.addEventListener( orig, handler, true );
},
teardown: function() {
this.removeEventListener( orig, handler, true );
}
};
function handler( e ) {
e = jQuery.event.fix( e );
e.type = fix;
return jQuery.event.handle.call( this, e );
}
});
}
jQuery.each(["bind", "one"], function( i, name ) {
jQuery.fn[ name ] = function( type, data, fn ) {
// Handle object literals
if ( typeof type === "object" ) {
for ( var key in type ) {
this[ name ](key, data, type[key], fn);
}
return this;
}
if ( jQuery.isFunction( data ) || data === false ) {
fn = data;
data = undefined;
}
var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
jQuery( this ).unbind( event, handler );
return fn.apply( this, arguments );
}) : fn;
if ( type === "unload" && name !== "one" ) {
this.one( type, data, fn );
} else {
for ( var i = 0, l = this.length; i < l; i++ ) {
jQuery.event.add( this[i], type, handler, data );
}
}
return this;
};
});
jQuery.fn.extend({
unbind: function( type, fn ) {
// Handle object literals
if ( typeof type === "object" && !type.preventDefault ) {
for ( var key in type ) {
this.unbind(key, type[key]);
}
} else {
for ( var i = 0, l = this.length; i < l; i++ ) {
jQuery.event.remove( this[i], type, fn );
}
}
return this;
},
delegate: function( selector, types, data, fn ) {
return this.live( types, data, fn, selector );
},
undelegate: function( selector, types, fn ) {
if ( arguments.length === 0 ) {
return this.unbind( "live" );
} else {
return this.die( types, null, fn, selector );
}
},
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.proxy( fn, args[ i++ ] );
}
return this.click( jQuery.proxy( fn, function( event ) {
// Figure out which function to execute
var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
// Make sure that clicks stop
event.preventDefault();
// and execute the function
return args[ lastToggle ].apply( this, arguments ) || false;
}));
},
hover: function( fnOver, fnOut ) {
return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
}
});
var liveMap = {
focus: "focusin",
blur: "focusout",
mouseenter: "mouseover",
mouseleave: "mouseout"
};
jQuery.each(["live", "die"], function( i, name ) {
jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
var type, i = 0, match, namespaces, preType,
selector = origSelector || this.selector,
context = origSelector ? this : jQuery( this.context );
if ( typeof types === "object" && !types.preventDefault ) {
for ( var key in types ) {
context[ name ]( key, data, types[key], selector );
}
return this;
}
if ( jQuery.isFunction( data ) ) {
fn = data;
data = undefined;
}
types = (types || "").split(" ");
while ( (type = types[ i++ ]) != null ) {
match = rnamespaces.exec( type );
namespaces = "";
if ( match ) {
namespaces = match[0];
type = type.replace( rnamespaces, "" );
}
if ( type === "hover" ) {
types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
continue;
}
preType = type;
if ( type === "focus" || type === "blur" ) {
types.push( liveMap[ type ] + namespaces );
type = type + namespaces;
} else {
type = (liveMap[ type ] || type) + namespaces;
}
if ( name === "live" ) {
// bind live handler
for ( var j = 0, l = context.length; j < l; j++ ) {
jQuery.event.add( context[j], "live." + liveConvert( type, selector ),
{ data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
}
} else {
// unbind live handler
context.unbind( "live." + liveConvert( type, selector ), fn );
}
}
return this;
};
});
function liveHandler( event ) {
var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
elems = [],
selectors = [],
events = jQuery._data( this, eventKey );
if ( typeof events === "function" ) {
events = events.events;
}
// Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911)
if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) {
return;
}
if ( event.namespace ) {
namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
}
event.liveFired = this;
var live = events.live.slice(0);
for ( j = 0; j < live.length; j++ ) {
handleObj = live[j];
if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
selectors.push( handleObj.selector );
} else {
live.splice( j--, 1 );
}
}
match = jQuery( event.target ).closest( selectors, event.currentTarget );
for ( i = 0, l = match.length; i < l; i++ ) {
close = match[i];
for ( j = 0; j < live.length; j++ ) {
handleObj = live[j];
if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) ) {
elem = close.elem;
related = null;
// Those two events require additional checking
if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
event.type = handleObj.preType;
related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
}
if ( !related || related !== elem ) {
elems.push({ elem: elem, handleObj: handleObj, level: close.level });
}
}
}
}
for ( i = 0, l = elems.length; i < l; i++ ) {
match = elems[i];
if ( maxLevel && match.level > maxLevel ) {
break;
}
event.currentTarget = match.elem;
event.data = match.handleObj.data;
event.handleObj = match.handleObj;
ret = match.handleObj.origHandler.apply( match.elem, arguments );
if ( ret === false || event.isPropagationStopped() ) {
maxLevel = match.level;
if ( ret === false ) {
stop = false;
}
if ( event.isImmediatePropagationStopped() ) {
break;
}
}
}
return stop;
}
function liveConvert( type, selector ) {
return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspace, "&");
}
jQuery.each( ("blur focus focusin focusout 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( data, fn ) {
if ( fn == null ) {
fn = data;
data = null;
}
return arguments.length > 0 ?
this.bind( name, data, fn ) :
this.trigger( name );
};
if ( jQuery.attrFn ) {
jQuery.attrFn[ name ] = true;
}
});
/*!
* Sizzle CSS Selector Engine
* Copyright 2011, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
* More information: http://sizzlejs.com/
*/
(function(){
var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
done = 0,
toString = Object.prototype.toString,
hasDuplicate = false,
baseHasDuplicate = true;
// Here we check if the JavaScript engine is using some sort of
// optimization where it does not always call our comparision
// function. If that is the case, discard the hasDuplicate value.
// Thus far that includes Google Chrome.
[0, 0].sort(function() {
baseHasDuplicate = false;
return 0;
});
var Sizzle = function( selector, context, results, seed ) {
results = results || [];
context = context || document;
var origContext = context;
if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
return [];
}
if ( !selector || typeof selector !== "string" ) {
return results;
}
var m, set, checkSet, extra, ret, cur, pop, i,
prune = true,
contextXML = Sizzle.isXML( context ),
parts = [],
soFar = selector;
// Reset the position of the chunker regexp (start from head)
do {
chunker.exec( "" );
m = chunker.exec( soFar );
if ( m ) {
soFar = m[3];
parts.push( m[1] );
if ( m[2] ) {
extra = m[3];
break;
}
}
} while ( m );
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 {
// Take a shortcut and set the context if the root selector is an ID
// (but not if it'll be faster if the inner selector is an ID)
if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
ret = Sizzle.find( parts.shift(), context, contextXML );
context = ret.expr ?
Sizzle.filter( ret.expr, ret.set )[0] :
ret.set[0];
}
if ( context ) {
ret = seed ?
{ expr: parts.pop(), set: makeArray(seed) } :
Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
set = ret.expr ?
Sizzle.filter( ret.expr, ret.set ) :
ret.set;
if ( parts.length > 0 ) {
checkSet = makeArray( set );
} else {
prune = false;
}
while ( parts.length ) {
cur = parts.pop();
pop = cur;
if ( !Expr.relative[ cur ] ) {
cur = "";
} else {
pop = parts.pop();
}
if ( pop == null ) {
pop = context;
}
Expr.relative[ cur ]( checkSet, pop, contextXML );
}
} else {
checkSet = parts = [];
}
}
if ( !checkSet ) {
checkSet = set;
}
if ( !checkSet ) {
Sizzle.error( cur || selector );
}
if ( toString.call(checkSet) === "[object Array]" ) {
if ( !prune ) {
results.push.apply( results, checkSet );
} else if ( context && context.nodeType === 1 ) {
for ( i = 0; checkSet[i] != null; i++ ) {
if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
results.push( set[i] );
}
}
} else {
for ( 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, origContext, results, seed );
Sizzle.uniqueSort( results );
}
return results;
};
Sizzle.uniqueSort = function( results ) {
if ( sortOrder ) {
hasDuplicate = baseHasDuplicate;
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.matchesSelector = function( node, expr ) {
return Sizzle( expr, null, null, [node] ).length > 0;
};
Sizzle.find = function( expr, context, isXML ) {
var set;
if ( !expr ) {
return [];
}
for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
var match,
type = Expr.order[i];
if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
var left = match[1];
match.splice( 1, 1 );
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 = typeof context.getElementsByTagName !== "undefined" ?
context.getElementsByTagName( "*" ) :
[];
}
return { set: set, expr: expr };
};
Sizzle.filter = function( expr, set, inplace, not ) {
var match, anyFound,
old = expr,
result = [],
curLoop = set,
isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
while ( expr && set.length ) {
for ( var type in Expr.filter ) {
if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
var found, item,
filter = Expr.filter[ type ],
left = match[1];
anyFound = false;
match.splice(1,1);
if ( left.substr( left.length - 1 ) === "\\" ) {
continue;
}
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 ) {
Sizzle.error( expr );
} else {
break;
}
}
old = expr;
}
return curLoop;
};
Sizzle.error = function( msg ) {
throw "Syntax error, unrecognized expression: " + msg;
};
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|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
},
leftMatch: {},
attrMap: {
"class": "className",
"for": "htmlFor"
},
attrHandle: {
href: function( elem ) {
return elem.getAttribute( "href" );
}
},
relative: {
"+": function(checkSet, part){
var isPartStr = typeof part === "string",
isTag = isPartStr && !/\W/.test( part ),
isPartStrNotTag = isPartStr && !isTag;
if ( isTag ) {
part = part.toLowerCase();
}
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.toLowerCase() === part ?
elem || false :
elem === part;
}
}
if ( isPartStrNotTag ) {
Sizzle.filter( part, checkSet, true );
}
},
">": function( checkSet, part ) {
var elem,
isPartStr = typeof part === "string",
i = 0,
l = checkSet.length;
if ( isPartStr && !/\W/.test( part ) ) {
part = part.toLowerCase();
for ( ; i < l; i++ ) {
elem = checkSet[i];
if ( elem ) {
var parent = elem.parentNode;
checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
}
}
} else {
for ( ; i < l; i++ ) {
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 nodeCheck,
doneName = done++,
checkFn = dirCheck;
if ( typeof part === "string" && !/\W/.test(part) ) {
part = part.toLowerCase();
nodeCheck = part;
checkFn = dirNodeCheck;
}
checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
},
"~": function( checkSet, part, isXML ) {
var nodeCheck,
doneName = done++,
checkFn = dirCheck;
if ( typeof part === "string" && !/\W/.test( part ) ) {
part = part.toLowerCase();
nodeCheck = part;
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]);
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
return m && m.parentNode ? [m] : [];
}
},
NAME: function( match, context ) {
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 ) {
if ( typeof context.getElementsByTagName !== "undefined" ) {
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 + " ").replace(/[\t\n\r]/g, " ").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 ) {
return match[1].toLowerCase();
},
CHILD: function( match ) {
if ( match[1] === "nth" ) {
if ( !match[2] ) {
Sizzle.error( match[0] );
}
match[2] = match[2].replace(/^\+|\s*/g, '');
// 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;
}
else if ( match[2] ) {
Sizzle.error( match[0] );
}
// TODO: Move to normal caching system
match[0] = done++;
return match;
},
ATTR: function( match, curLoop, inplace, result, not, isXML ) {
var name = match[1] = match[1].replace(/\\/g, "");
if ( !isXML && Expr.attrMap[name] ) {
match[1] = Expr.attrMap[name];
}
// Handle if an un-quoted value was used
match[4] = ( match[4] || match[5] || "" ).replace(/\\/g, "");
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 ( ( chunker.exec(match[3]) || "" ).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.toLowerCase() === "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 || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;
} else if ( name === "not" ) {
var not = match[3];
for ( var j = 0, l = not.length; j < l; j++ ) {
if ( not[j] === elem ) {
return false;
}
}
return true;
} else {
Sizzle.error( name );
}
},
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.toLowerCase() === 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,
fescape = function(all, num){
return "\\" + (num - 0 + 1);
};
for ( var type in Expr.match ) {
Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
}
var makeArray = function( array, results ) {
array = Array.prototype.slice.call( array, 0 );
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.
// Also verifies that the returned array holds DOM nodes
// (which is not the case in the Blackberry browser)
try {
Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
// Provide a fallback method if it does not work
} catch( e ) {
makeArray = function( array, results ) {
var i = 0,
ret = results || [];
if ( toString.call(array) === "[object Array]" ) {
Array.prototype.push.apply( ret, array );
} else {
if ( typeof array.length === "number" ) {
for ( var l = array.length; i < l; i++ ) {
ret.push( array[i] );
}
} else {
for ( ; array[i]; i++ ) {
ret.push( array[i] );
}
}
}
return ret;
};
}
var sortOrder, siblingCheck;
if ( document.documentElement.compareDocumentPosition ) {
sortOrder = function( a, b ) {
if ( a === b ) {
hasDuplicate = true;
return 0;
}
if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
return a.compareDocumentPosition ? -1 : 1;
}
return a.compareDocumentPosition(b) & 4 ? -1 : 1;
};
} else {
sortOrder = function( a, b ) {
var al, bl,
ap = [],
bp = [],
aup = a.parentNode,
bup = b.parentNode,
cur = aup;
// The nodes are identical, we can exit early
if ( a === b ) {
hasDuplicate = true;
return 0;
// If the nodes are siblings (or identical) we can do a quick check
} else if ( aup === bup ) {
return siblingCheck( a, b );
// If no parents were found then the nodes are disconnected
} else if ( !aup ) {
return -1;
} else if ( !bup ) {
return 1;
}
// Otherwise they're somewhere else in the tree so we need
// to build up a full list of the parentNodes for comparison
while ( cur ) {
ap.unshift( cur );
cur = cur.parentNode;
}
cur = bup;
while ( cur ) {
bp.unshift( cur );
cur = cur.parentNode;
}
al = ap.length;
bl = bp.length;
// Start walking down the tree looking for a discrepancy
for ( var i = 0; i < al && i < bl; i++ ) {
if ( ap[i] !== bp[i] ) {
return siblingCheck( ap[i], bp[i] );
}
}
// We ended someplace up the tree so do a sibling check
return i === al ?
siblingCheck( a, bp[i], -1 ) :
siblingCheck( ap[i], b, 1 );
};
siblingCheck = function( a, b, ret ) {
if ( a === b ) {
return ret;
}
var cur = a.nextSibling;
while ( cur ) {
if ( cur === b ) {
return -1;
}
cur = cur.nextSibling;
}
return 1;
};
}
// Utility function for retreiving the text value of an array of DOM nodes
Sizzle.getText = function( elems ) {
var ret = "", elem;
for ( var i = 0; elems[i]; i++ ) {
elem = elems[i];
// Get the text from text nodes and CDATA nodes
if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
ret += elem.nodeValue;
// Traverse everything else, except comment nodes
} else if ( elem.nodeType !== 8 ) {
ret += Sizzle.getText( elem.childNodes );
}
}
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("div"),
id = "script" + (new Date()).getTime(),
root = document.documentElement;
form.innerHTML = "<a name='" + id + "'/>";
// Inject it into the root element, check its status, and remove it quickly
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 );
// release memory in IE
root = form = null;
})();
(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 );
};
}
// release memory in IE
div = null;
})();
if ( document.querySelectorAll ) {
(function(){
var oldSizzle = Sizzle,
div = document.createElement("div"),
id = "__sizzle__";
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 && !Sizzle.isXML(context) ) {
// See if we find a selector to speed up
var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
// Speed-up: Sizzle("TAG")
if ( match[1] ) {
return makeArray( context.getElementsByTagName( query ), extra );
// Speed-up: Sizzle(".CLASS")
} else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
return makeArray( context.getElementsByClassName( match[2] ), extra );
}
}
if ( context.nodeType === 9 ) {
// Speed-up: Sizzle("body")
// The body element only exists once, optimize finding it
if ( query === "body" && context.body ) {
return makeArray( [ context.body ], extra );
// Speed-up: Sizzle("#ID")
} else if ( match && match[3] ) {
var elem = context.getElementById( match[3] );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
if ( elem && elem.parentNode ) {
// Handle the case where IE and Opera return items
// by name instead of ID
if ( elem.id === match[3] ) {
return makeArray( [ elem ], extra );
}
} else {
return makeArray( [], extra );
}
}
try {
return makeArray( context.querySelectorAll(query), extra );
} catch(qsaError) {}
// qSA works strangely on Element-rooted queries
// We can work around this by specifying an extra ID on the root
// and working up from there (Thanks to Andrew Dupont for the technique)
// IE 8 doesn't work on object elements
} else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
var old = context.getAttribute( "id" ),
nid = old || id,
hasParent = context.parentNode,
relativeHierarchySelector = /^\s*[+~]/.test( query );
if ( !old ) {
context.setAttribute( "id", nid );
} else {
nid = nid.replace( /'/g, "\\$&" );
}
if ( relativeHierarchySelector && hasParent ) {
context = context.parentNode;
}
try {
if ( !relativeHierarchySelector || hasParent ) {
return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
}
} catch(pseudoError) {
} finally {
if ( !old ) {
context.removeAttribute( "id" );
}
}
}
}
return oldSizzle(query, context, extra, seed);
};
for ( var prop in oldSizzle ) {
Sizzle[ prop ] = oldSizzle[ prop ];
}
// release memory in IE
div = null;
})();
}
(function(){
var html = document.documentElement,
matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector,
pseudoWorks = false;
try {
// This should fail with an exception
// Gecko does not error, returns false instead
matches.call( document.documentElement, "[test!='']:sizzle" );
} catch( pseudoError ) {
pseudoWorks = true;
}
if ( matches ) {
Sizzle.matchesSelector = function( node, expr ) {
// Make sure that attribute selectors are quoted
expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
if ( !Sizzle.isXML( node ) ) {
try {
if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
return matches.call( node, expr );
}
} catch(e) {}
}
return Sizzle(expr, null, null, [node]).length > 0;
};
}
})();
(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)
// Also, make sure that getElementsByClassName actually exists
if ( !div.getElementsByClassName || 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]);
}
};
// release memory in IE
div = null;
})();
function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
if ( elem ) {
var match = false;
elem = elem[dir];
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.toLowerCase() === cur ) {
match = elem;
break;
}
elem = elem[dir];
}
checkSet[i] = match;
}
}
}
function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
if ( elem ) {
var match = false;
elem = elem[dir];
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;
}
}
}
if ( document.documentElement.contains ) {
Sizzle.contains = function( a, b ) {
return a !== b && (a.contains ? a.contains(b) : true);
};
} else if ( document.documentElement.compareDocumentPosition ) {
Sizzle.contains = function( a, b ) {
return !!(a.compareDocumentPosition(b) & 16);
};
} else {
Sizzle.contains = function() {
return false;
};
}
Sizzle.isXML = function( elem ) {
// documentElement is verified for cases where it doesn't yet exist
// (such as loading iframes in IE - #4833)
var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
return documentElement ? documentElement.nodeName !== "HTML" : false;
};
var posProcess = function( selector, context ) {
var match,
tmpSet = [],
later = "",
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.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.filters;
jQuery.unique = Sizzle.uniqueSort;
jQuery.text = Sizzle.getText;
jQuery.isXMLDoc = Sizzle.isXML;
jQuery.contains = Sizzle.contains;
})();
var runtil = /Until$/,
rparentsprev = /^(?:parents|prevUntil|prevAll)/,
// Note: This RegExp should be improved, or likely pulled from Sizzle
rmultiselector = /,/,
isSimple = /^.[^:#\[\.,]*$/,
slice = Array.prototype.slice,
POS = jQuery.expr.match.POS,
// methods guaranteed to produce a unique set when starting from a unique set
guaranteedUnique = {
children: true,
contents: true,
next: true,
prev: true
};
jQuery.fn.extend({
find: function( selector ) {
var ret = this.pushStack( "", "find", selector ),
length = 0;
for ( var i = 0, l = this.length; i < l; i++ ) {
length = ret.length;
jQuery.find( selector, this[i], ret );
if ( i > 0 ) {
// Make sure that the results are unique
for ( var n = length; n < ret.length; n++ ) {
for ( var r = 0; r < length; r++ ) {
if ( ret[r] === ret[n] ) {
ret.splice(n--, 1);
break;
}
}
}
}
}
return ret;
},
has: function( target ) {
var targets = jQuery( target );
return this.filter(function() {
for ( var i = 0, l = targets.length; i < l; i++ ) {
if ( jQuery.contains( this, targets[i] ) ) {
return true;
}
}
});
},
not: function( selector ) {
return this.pushStack( winnow(this, selector, false), "not", selector);
},
filter: function( selector ) {
return this.pushStack( winnow(this, selector, true), "filter", selector );
},
is: function( selector ) {
return !!selector && jQuery.filter( selector, this ).length > 0;
},
closest: function( selectors, context ) {
var ret = [], i, l, cur = this[0];
if ( jQuery.isArray( selectors ) ) {
var match, selector,
matches = {},
level = 1;
if ( cur && selectors.length ) {
for ( i = 0, l = selectors.length; i < l; i++ ) {
selector = selectors[i];
if ( !matches[selector] ) {
matches[selector] = jQuery.expr.match.POS.test( selector ) ?
jQuery( selector, context || this.context ) :
selector;
}
}
while ( cur && cur.ownerDocument && cur !== context ) {
for ( selector in matches ) {
match = matches[selector];
if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) {
ret.push({ selector: selector, elem: cur, level: level });
}
}
cur = cur.parentNode;
level++;
}
}
return ret;
}
var pos = POS.test( selectors ) ?
jQuery( selectors, context || this.context ) : null;
for ( i = 0, l = this.length; i < l; i++ ) {
cur = this[i];
while ( cur ) {
if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
ret.push( cur );
break;
} else {
cur = cur.parentNode;
if ( !cur || !cur.ownerDocument || cur === context ) {
break;
}
}
}
}
ret = ret.length > 1 ? jQuery.unique(ret) : ret;
return this.pushStack( ret, "closest", selectors );
},
// Determine the position of an element within
// the matched set of elements
index: function( elem ) {
if ( !elem || typeof elem === "string" ) {
return jQuery.inArray( this[0],
// If it receives a string, the selector is used
// If it receives nothing, the siblings are used
elem ? jQuery( elem ) : this.parent().children() );
}
// Locate the position of the desired element
return jQuery.inArray(
// If it receives a jQuery object, the first element is used
elem.jquery ? elem[0] : elem, this );
},
add: function( selector, context ) {
var set = typeof selector === "string" ?
jQuery( selector, context ) :
jQuery.makeArray( selector ),
all = jQuery.merge( this.get(), set );
return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
all :
jQuery.unique( all ) );
},
andSelf: function() {
return this.add( this.prevObject );
}
});
// A painfully simple check to see if an element is disconnected
// from a document (should be improved, where feasible).
function isDisconnected( node ) {
return !node || !node.parentNode || node.parentNode.nodeType === 11;
}
jQuery.each({
parent: function( elem ) {
var parent = elem.parentNode;
return parent && parent.nodeType !== 11 ? parent : null;
},
parents: function( elem ) {
return jQuery.dir( elem, "parentNode" );
},
parentsUntil: function( elem, i, until ) {
return jQuery.dir( elem, "parentNode", until );
},
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" );
},
nextUntil: function( elem, i, until ) {
return jQuery.dir( elem, "nextSibling", until );
},
prevUntil: function( elem, i, until ) {
return jQuery.dir( elem, "previousSibling", until );
},
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( until, selector ) {
var ret = jQuery.map( this, fn, until ),
// The variable 'args' was introduced in
// https://github.com/jquery/jquery/commit/52a0238
// to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed.
// http://code.google.com/p/v8/issues/detail?id=1050
args = slice.call(arguments);
if ( !runtil.test( name ) ) {
selector = until;
}
if ( selector && typeof selector === "string" ) {
ret = jQuery.filter( selector, ret );
}
ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
ret = ret.reverse();
}
return this.pushStack( ret, name, args.join(",") );
};
});
jQuery.extend({
filter: function( expr, elems, not ) {
if ( not ) {
expr = ":not(" + expr + ")";
}
return elems.length === 1 ?
jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
jQuery.find.matches(expr, elems);
},
dir: function( elem, dir, until ) {
var matched = [],
cur = elem[ dir ];
while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
if ( cur.nodeType === 1 ) {
matched.push( cur );
}
cur = cur[dir];
}
return matched;
},
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;
},
sibling: function( n, elem ) {
var r = [];
for ( ; n; n = n.nextSibling ) {
if ( n.nodeType === 1 && n !== elem ) {
r.push( n );
}
}
return r;
}
});
// Implement the identical functionality for filter and not
function winnow( elements, qualifier, keep ) {
if ( jQuery.isFunction( qualifier ) ) {
return jQuery.grep(elements, function( elem, i ) {
var retVal = !!qualifier.call( elem, i, elem );
return retVal === keep;
});
} else if ( qualifier.nodeType ) {
return jQuery.grep(elements, function( elem, i ) {
return (elem === qualifier) === keep;
});
} else if ( typeof qualifier === "string" ) {
var filtered = jQuery.grep(elements, function( elem ) {
return elem.nodeType === 1;
});
if ( isSimple.test( qualifier ) ) {
return jQuery.filter(qualifier, filtered, !keep);
} else {
qualifier = jQuery.filter( qualifier, filtered );
}
}
return jQuery.grep(elements, function( elem, i ) {
return (jQuery.inArray( elem, qualifier ) >= 0) === keep;
});
}
var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
rleadingWhitespace = /^\s+/,
rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
rtagName = /<([\w:]+)/,
rtbody = /<tbody/i,
rhtml = /<|&#?\w+;/,
rnocache = /<(?:script|object|embed|option|style)/i,
// checked="checked" or checked (html5)
rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
wrapMap = {
option: [ 1, "<select multiple='multiple'>", "</select>" ],
legend: [ 1, "<fieldset>", "</fieldset>" ],
thead: [ 1, "<table>", "</table>" ],
tr: [ 2, "<table><tbody>", "</tbody></table>" ],
td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
area: [ 1, "<map>", "</map>" ],
_default: [ 0, "", "" ]
};
wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;
// IE can't serialize <link> and <script> tags normally
if ( !jQuery.support.htmlSerialize ) {
wrapMap._default = [ 1, "div<div>", "</div>" ];
}
jQuery.fn.extend({
text: function( text ) {
if ( jQuery.isFunction(text) ) {
return this.each(function(i) {
var self = jQuery( this );
self.text( text.call(this, i, self.text()) );
});
}
if ( typeof text !== "object" && text !== undefined ) {
return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
}
return jQuery.text( this );
},
wrapAll: function( html ) {
if ( jQuery.isFunction( html ) ) {
return this.each(function(i) {
jQuery(this).wrapAll( html.call(this, i) );
});
}
if ( this[0] ) {
// The elements to wrap the target around
var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
if ( this[0].parentNode ) {
wrap.insertBefore( this[0] );
}
wrap.map(function() {
var elem = this;
while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
elem = elem.firstChild;
}
return elem;
}).append(this);
}
return this;
},
wrapInner: function( html ) {
if ( jQuery.isFunction( html ) ) {
return this.each(function(i) {
jQuery(this).wrapInner( html.call(this, i) );
});
}
return this.each(function() {
var self = jQuery( this ),
contents = self.contents();
if ( contents.length ) {
contents.wrapAll( html );
} else {
self.append( html );
}
});
},
wrap: function( html ) {
return this.each(function() {
jQuery( this ).wrapAll( html );
});
},
unwrap: function() {
return this.parent().each(function() {
if ( !jQuery.nodeName( this, "body" ) ) {
jQuery( this ).replaceWith( this.childNodes );
}
}).end();
},
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() {
if ( this[0] && this[0].parentNode ) {
return this.domManip(arguments, false, function( elem ) {
this.parentNode.insertBefore( elem, this );
});
} else if ( arguments.length ) {
var set = jQuery(arguments[0]);
set.push.apply( set, this.toArray() );
return this.pushStack( set, "before", arguments );
}
},
after: function() {
if ( this[0] && this[0].parentNode ) {
return this.domManip(arguments, false, function( elem ) {
this.parentNode.insertBefore( elem, this.nextSibling );
});
} else if ( arguments.length ) {
var set = this.pushStack( this, "after", arguments );
set.push.apply( set, jQuery(arguments[0]).toArray() );
return set;
}
},
// keepData is for internal use only--do not document
remove: function( selector, keepData ) {
for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
if ( !keepData && elem.nodeType === 1 ) {
jQuery.cleanData( elem.getElementsByTagName("*") );
jQuery.cleanData( [ elem ] );
}
if ( elem.parentNode ) {
elem.parentNode.removeChild( elem );
}
}
}
return this;
},
empty: function() {
for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
// Remove element nodes and prevent memory leaks
if ( elem.nodeType === 1 ) {
jQuery.cleanData( elem.getElementsByTagName("*") );
}
// Remove any remaining nodes
while ( elem.firstChild ) {
elem.removeChild( elem.firstChild );
}
}
return this;
},
clone: function( dataAndEvents, deepDataAndEvents ) {
dataAndEvents = dataAndEvents == null ? true : dataAndEvents;
deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
return this.map( function () {
return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
});
},
html: function( value ) {
if ( value === undefined ) {
return this[0] && this[0].nodeType === 1 ?
this[0].innerHTML.replace(rinlinejQuery, "") :
null;
// See if we can take a shortcut and just use innerHTML
} else if ( typeof value === "string" && !rnocache.test( value ) &&
(jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
!wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
value = value.replace(rxhtmlTag, "<$1></$2>");
try {
for ( var i = 0, l = this.length; i < l; i++ ) {
// Remove element nodes and prevent memory leaks
if ( this[i].nodeType === 1 ) {
jQuery.cleanData( this[i].getElementsByTagName("*") );
this[i].innerHTML = value;
}
}
// If using innerHTML throws an exception, use the fallback method
} catch(e) {
this.empty().append( value );
}
} else if ( jQuery.isFunction( value ) ) {
this.each(function(i){
var self = jQuery( this );
self.html( value.call(this, i, self.html()) );
});
} else {
this.empty().append( value );
}
return this;
},
replaceWith: function( value ) {
if ( this[0] && this[0].parentNode ) {
// Make sure that the elements are removed from the DOM before they are inserted
// this can help fix replacing a parent with child elements
if ( jQuery.isFunction( value ) ) {
return this.each(function(i) {
var self = jQuery(this), old = self.html();
self.replaceWith( value.call( this, i, old ) );
});
}
if ( typeof value !== "string" ) {
value = jQuery( value ).detach();
}
return this.each(function() {
var next = this.nextSibling,
parent = this.parentNode;
jQuery( this ).remove();
if ( next ) {
jQuery(next).before( value );
} else {
jQuery(parent).append( value );
}
});
} else {
return this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value );
}
},
detach: function( selector ) {
return this.remove( selector, true );
},
domManip: function( args, table, callback ) {
var results, first, fragment, parent,
value = args[0],
scripts = [];
// We can't cloneNode fragments that contain checked, in WebKit
if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
return this.each(function() {
jQuery(this).domManip( args, table, callback, true );
});
}
if ( jQuery.isFunction(value) ) {
return this.each(function(i) {
var self = jQuery(this);
args[0] = value.call(this, i, table ? self.html() : undefined);
self.domManip( args, table, callback );
});
}
if ( this[0] ) {
parent = value && value.parentNode;
// If we're in a fragment, just use that instead of building a new one
if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
results = { fragment: parent };
} else {
results = jQuery.buildFragment( args, this, scripts );
}
fragment = results.fragment;
if ( fragment.childNodes.length === 1 ) {
first = fragment = fragment.firstChild;
} else {
first = fragment.firstChild;
}
if ( first ) {
table = table && jQuery.nodeName( first, "tr" );
for ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) {
callback.call(
table ?
root(this[i], first) :
this[i],
// Make sure that we do not leak memory by inadvertently discarding
// the original fragment (which might have attached data) instead of
// using it; in addition, use the original fragment object for the last
// item instead of first because it can end up being emptied incorrectly
// in certain situations (Bug #8070).
// Fragments from the fragment cache must always be cloned and never used
// in place.
results.cacheable || (l > 1 && i < lastIndex) ?
jQuery.clone( fragment, true, true ) :
fragment
);
}
}
if ( scripts.length ) {
jQuery.each( scripts, evalScript );
}
}
return this;
}
});
function root( elem, cur ) {
return jQuery.nodeName(elem, "table") ?
(elem.getElementsByTagName("tbody")[0] ||
elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
elem;
}
function cloneCopyEvent( src, dest ) {
if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
return;
}
var internalKey = jQuery.expando,
oldData = jQuery.data( src ),
curData = jQuery.data( dest, oldData );
// Switch to use the internal data object, if it exists, for the next
// stage of data copying
if ( (oldData = oldData[ internalKey ]) ) {
var events = oldData.events;
curData = curData[ internalKey ] = jQuery.extend({}, oldData);
if ( events ) {
delete curData.handle;
curData.events = {};
for ( var type in events ) {
for ( var i = 0, l = events[ type ].length; i < l; i++ ) {
jQuery.event.add( dest, type, events[ type ][ i ], events[ type ][ i ].data );
}
}
}
}
}
function cloneFixAttributes(src, dest) {
// We do not need to do anything for non-Elements
if ( dest.nodeType !== 1 ) {
return;
}
var nodeName = dest.nodeName.toLowerCase();
// clearAttributes removes the attributes, which we don't want,
// but also removes the attachEvent events, which we *do* want
dest.clearAttributes();
// mergeAttributes, in contrast, only merges back on the
// original attributes, not the events
dest.mergeAttributes(src);
// IE6-8 fail to clone children inside object elements that use
// the proprietary classid attribute value (rather than the type
// attribute) to identify the type of content to display
if ( nodeName === "object" ) {
dest.outerHTML = src.outerHTML;
} else if ( nodeName === "input" && (src.type === "checkbox" || src.type === "radio") ) {
// IE6-8 fails to persist the checked state of a cloned checkbox
// or radio button. Worse, IE6-7 fail to give the cloned element
// a checked appearance if the defaultChecked value isn't also set
if ( src.checked ) {
dest.defaultChecked = dest.checked = src.checked;
}
// IE6-7 get confused and end up setting the value of a cloned
// checkbox/radio button to an empty string instead of "on"
if ( dest.value !== src.value ) {
dest.value = src.value;
}
// IE6-8 fails to return the selected option to the default selected
// state when cloning options
} else if ( nodeName === "option" ) {
dest.selected = src.defaultSelected;
// IE6-8 fails to set the defaultValue to the correct value when
// cloning other types of input fields
} else if ( nodeName === "input" || nodeName === "textarea" ) {
dest.defaultValue = src.defaultValue;
}
// Event data gets referenced instead of copied if the expando
// gets copied too
dest.removeAttribute( jQuery.expando );
}
jQuery.buildFragment = function( args, nodes, scripts ) {
var fragment, cacheable, cacheresults,
doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);
// Only cache "small" (1/2 KB) HTML strings that are associated with the main document
// Cloning options loses the selected state, so don't cache them
// IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
// Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&
args[0].charAt(0) === "<" && !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
cacheable = true;
cacheresults = jQuery.fragments[ args[0] ];
if ( cacheresults ) {
if ( cacheresults !== 1 ) {
fragment = cacheresults;
}
}
}
if ( !fragment ) {
fragment = doc.createDocumentFragment();
jQuery.clean( args, doc, fragment, scripts );
}
if ( cacheable ) {
jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;
}
return { fragment: fragment, cacheable: cacheable };
};
jQuery.fragments = {};
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 ),
parent = this.length === 1 && this[0].parentNode;
if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
insert[ original ]( this[0] );
return this;
} else {
for ( var i = 0, l = insert.length; i < l; i++ ) {
var elems = (i > 0 ? this.clone(true) : this).get();
jQuery( insert[i] )[ original ]( elems );
ret = ret.concat( elems );
}
return this.pushStack( ret, name, insert.selector );
}
};
});
jQuery.extend({
clone: function( elem, dataAndEvents, deepDataAndEvents ) {
var clone = elem.cloneNode(true),
srcElements,
destElements,
i;
if ( !jQuery.support.noCloneEvent && (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
// IE copies events bound via attachEvent when using cloneNode.
// Calling detachEvent on the clone will also remove the events
// from the original. In order to get around this, we use some
// proprietary methods to clear the events. Thanks to MooTools
// guys for this hotness.
// Using Sizzle here is crazy slow, so we use getElementsByTagName
// instead
srcElements = elem.getElementsByTagName("*");
destElements = clone.getElementsByTagName("*");
// Weird iteration because IE will replace the length property
// with an element if you are cloning the body and one of the
// elements on the page has a name or id of "length"
for ( i = 0; srcElements[i]; ++i ) {
cloneFixAttributes( srcElements[i], destElements[i] );
}
cloneFixAttributes( elem, clone );
}
// Copy the events from the original to the clone
if ( dataAndEvents ) {
cloneCopyEvent( elem, clone );
if ( deepDataAndEvents && "getElementsByTagName" in elem ) {
srcElements = elem.getElementsByTagName("*");
destElements = clone.getElementsByTagName("*");
if ( srcElements.length ) {
for ( i = 0; srcElements[i]; ++i ) {
cloneCopyEvent( srcElements[i], destElements[i] );
}
}
}
}
// Return the cloned set
return clone;
},
clean: function( elems, context, fragment, scripts ) {
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;
}
var ret = [];
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
if ( typeof elem === "number" ) {
elem += "";
}
if ( !elem ) {
continue;
}
// Convert html string into DOM nodes
if ( typeof elem === "string" && !rhtml.test( elem ) ) {
elem = context.createTextNode( elem );
} else if ( typeof elem === "string" ) {
// Fix "XHTML"-style tags in all browsers
elem = elem.replace(rxhtmlTag, "<$1></$2>");
// Trim whitespace, otherwise indexOf won't work as expected
var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
wrap = wrapMap[ tag ] || wrapMap._default,
depth = wrap[0],
div = context.createElement("div");
// Go to html and back, then peel off extra wrappers
div.innerHTML = wrap[1] + elem + wrap[2];
// Move to the right depth
while ( depth-- ) {
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 = rtbody.test(elem),
tbody = tag === "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 && rleadingWhitespace.test( elem ) ) {
div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
}
elem = div.childNodes;
}
if ( elem.nodeType ) {
ret.push( elem );
} else {
ret = jQuery.merge( ret, elem );
}
}
if ( fragment ) {
for ( i = 0; ret[i]; i++ ) {
if ( scripts && 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 ret;
},
cleanData: function( elems ) {
var data, id, cache = jQuery.cache, internalKey = jQuery.expando, special = jQuery.event.special,
deleteExpando = jQuery.support.deleteExpando;
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
continue;
}
id = elem[ jQuery.expando ];
if ( id ) {
data = cache[ id ] && cache[ id ][ internalKey ];
if ( data && data.events ) {
for ( var type in data.events ) {
if ( special[ type ] ) {
jQuery.event.remove( elem, type );
// This is a shortcut to avoid jQuery.event.remove's overhead
} else {
jQuery.removeEvent( elem, type, data.handle );
}
}
// Null the DOM reference to avoid IE6/7/8 leak (#7054)
if ( data.handle ) {
data.handle.elem = null;
}
}
if ( deleteExpando ) {
delete elem[ jQuery.expando ];
} else if ( elem.removeAttribute ) {
elem.removeAttribute( jQuery.expando );
}
delete cache[ id ];
}
}
}
});
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 );
}
}
var ralpha = /alpha\([^)]*\)/i,
ropacity = /opacity=([^)]*)/,
rdashAlpha = /-([a-z])/ig,
rupper = /([A-Z])/g,
rnumpx = /^-?\d+(?:px)?$/i,
rnum = /^-?\d/,
cssShow = { position: "absolute", visibility: "hidden", display: "block" },
cssWidth = [ "Left", "Right" ],
cssHeight = [ "Top", "Bottom" ],
curCSS,
getComputedStyle,
currentStyle,
fcamelCase = function( all, letter ) {
return letter.toUpperCase();
};
jQuery.fn.css = function( name, value ) {
// Setting 'undefined' is a no-op
if ( arguments.length === 2 && value === undefined ) {
return this;
}
return jQuery.access( this, name, value, true, function( elem, name, value ) {
return value !== undefined ?
jQuery.style( elem, name, value ) :
jQuery.css( elem, name );
});
};
jQuery.extend({
// Add in style property hooks for overriding the default
// behavior of getting and setting a style property
cssHooks: {
opacity: {
get: function( elem, computed ) {
if ( computed ) {
// We should always get a number back from opacity
var ret = curCSS( elem, "opacity", "opacity" );
return ret === "" ? "1" : ret;
} else {
return elem.style.opacity;
}
}
}
},
// Exclude the following css properties to add px
cssNumber: {
"zIndex": true,
"fontWeight": true,
"opacity": true,
"zoom": true,
"lineHeight": true
},
// Add in properties whose names you wish to fix before
// setting or getting the value
cssProps: {
// normalize float css property
"float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
},
// Get and set the style property on a DOM Node
style: function( elem, name, value, extra ) {
// Don't set styles on text and comment nodes
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
return;
}
// Make sure that we're working with the right name
var ret, origName = jQuery.camelCase( name ),
style = elem.style, hooks = jQuery.cssHooks[ origName ];
name = jQuery.cssProps[ origName ] || origName;
// Check if we're setting a value
if ( value !== undefined ) {
// Make sure that NaN and null values aren't set. See: #7116
if ( typeof value === "number" && isNaN( value ) || value == null ) {
return;
}
// If a number was passed in, add 'px' to the (except for certain CSS properties)
if ( typeof value === "number" && !jQuery.cssNumber[ origName ] ) {
value += "px";
}
// If a hook was provided, use that value, otherwise just set the specified value
if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {
// Wrapped to prevent IE from throwing errors when 'invalid' values are provided
// Fixes bug #5509
try {
style[ name ] = value;
} catch(e) {}
}
} else {
// If a hook was provided get the non-computed value from there
if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
return ret;
}
// Otherwise just get the value from the style object
return style[ name ];
}
},
css: function( elem, name, extra ) {
// Make sure that we're working with the right name
var ret, origName = jQuery.camelCase( name ),
hooks = jQuery.cssHooks[ origName ];
name = jQuery.cssProps[ origName ] || origName;
// If a hook was provided get the computed value from there
if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) {
return ret;
// Otherwise, if a way to get the computed value exists, use that
} else if ( curCSS ) {
return curCSS( elem, name, origName );
}
},
// 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 ( name in options ) {
elem.style[ name ] = old[ name ];
}
},
camelCase: function( string ) {
return string.replace( rdashAlpha, fcamelCase );
}
});
// DEPRECATED, Use jQuery.css() instead
jQuery.curCSS = jQuery.css;
jQuery.each(["height", "width"], function( i, name ) {
jQuery.cssHooks[ name ] = {
get: function( elem, computed, extra ) {
var val;
if ( computed ) {
if ( elem.offsetWidth !== 0 ) {
val = getWH( elem, name, extra );
} else {
jQuery.swap( elem, cssShow, function() {
val = getWH( elem, name, extra );
});
}
if ( val <= 0 ) {
val = curCSS( elem, name, name );
if ( val === "0px" && currentStyle ) {
val = currentStyle( elem, name, name );
}
if ( val != null ) {
// Should return "auto" instead of 0, use 0 for
// temporary backwards-compat
return val === "" || val === "auto" ? "0px" : val;
}
}
if ( val < 0 || val == null ) {
val = elem.style[ name ];
// Should return "auto" instead of 0, use 0 for
// temporary backwards-compat
return val === "" || val === "auto" ? "0px" : val;
}
return typeof val === "string" ? val : val + "px";
}
},
set: function( elem, value ) {
if ( rnumpx.test( value ) ) {
// ignore negative width and height values #1599
value = parseFloat(value);
if ( value >= 0 ) {
return value + "px";
}
} else {
return value;
}
}
};
});
if ( !jQuery.support.opacity ) {
jQuery.cssHooks.opacity = {
get: function( elem, computed ) {
// IE uses filters for opacity
return ropacity.test((computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "") ?
(parseFloat(RegExp.$1) / 100) + "" :
computed ? "1" : "";
},
set: function( elem, value ) {
var style = elem.style;
// IE has trouble with opacity if it does not have layout
// Force it by setting the zoom level
style.zoom = 1;
// Set the alpha filter to set the opacity
var opacity = jQuery.isNaN(value) ?
"" :
"alpha(opacity=" + value * 100 + ")",
filter = style.filter || "";
style.filter = ralpha.test(filter) ?
filter.replace(ralpha, opacity) :
style.filter + ' ' + opacity;
}
};
}
if ( document.defaultView && document.defaultView.getComputedStyle ) {
getComputedStyle = function( elem, newName, name ) {
var ret, defaultView, computedStyle;
name = name.replace( rupper, "-$1" ).toLowerCase();
if ( !(defaultView = elem.ownerDocument.defaultView) ) {
return undefined;
}
if ( (computedStyle = defaultView.getComputedStyle( elem, null )) ) {
ret = computedStyle.getPropertyValue( name );
if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
ret = jQuery.style( elem, name );
}
}
return ret;
};
}
if ( document.documentElement.currentStyle ) {
currentStyle = function( elem, name ) {
var left,
ret = elem.currentStyle && elem.currentStyle[ name ],
rsLeft = elem.runtimeStyle && elem.runtimeStyle[ name ],
style = elem.style;
// 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 ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
// Remember the original values
left = style.left;
// Put in the new values to get a computed value out
if ( rsLeft ) {
elem.runtimeStyle.left = elem.currentStyle.left;
}
style.left = name === "fontSize" ? "1em" : (ret || 0);
ret = style.pixelLeft + "px";
// Revert the changed values
style.left = left;
if ( rsLeft ) {
elem.runtimeStyle.left = rsLeft;
}
}
return ret === "" ? "auto" : ret;
};
}
curCSS = getComputedStyle || currentStyle;
function getWH( elem, name, extra ) {
var which = name === "width" ? cssWidth : cssHeight,
val = name === "width" ? elem.offsetWidth : elem.offsetHeight;
if ( extra === "border" ) {
return val;
}
jQuery.each( which, function() {
if ( !extra ) {
val -= parseFloat(jQuery.css( elem, "padding" + this )) || 0;
}
if ( extra === "margin" ) {
val += parseFloat(jQuery.css( elem, "margin" + this )) || 0;
} else {
val -= parseFloat(jQuery.css( elem, "border" + this + "Width" )) || 0;
}
});
return val;
}
if ( jQuery.expr && jQuery.expr.filters ) {
jQuery.expr.filters.hidden = function( elem ) {
var width = elem.offsetWidth,
height = elem.offsetHeight;
return (width === 0 && height === 0) || (!jQuery.support.reliableHiddenOffsets && (elem.style.display || jQuery.css( elem, "display" )) === "none");
};
jQuery.expr.filters.visible = function( elem ) {
return !jQuery.expr.filters.hidden( elem );
};
}
var r20 = /%20/g,
rbracket = /\[\]$/,
rCRLF = /\r?\n/g,
rhash = /#.*$/,
rheaders = /^(.*?):\s*(.*?)\r?$/mg, // IE leaves an \r character at EOL
rinput = /^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
rnoContent = /^(?:GET|HEAD)$/,
rprotocol = /^\/\//,
rquery = /\?/,
rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
rselectTextarea = /^(?:select|textarea)/i,
rspacesAjax = /\s+/,
rts = /([?&])_=[^&]*/,
rurl = /^(\w+:)\/\/([^\/?#:]+)(?::(\d+))?/,
// Keep a copy of the old load method
_load = jQuery.fn.load,
/* Prefilters
* 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
* 2) These are called:
* - BEFORE asking for a transport
* - AFTER param serialization (s.data is a string if s.processData is true)
* 3) key is the dataType
* 4) the catchall symbol "*" can be used
* 5) execution will start with transport dataType and THEN continue down to "*" if needed
*/
prefilters = {},
/* Transports bindings
* 1) key is the dataType
* 2) the catchall symbol "*" can be used
* 3) selection will start with transport dataType and THEN go to "*" if needed
*/
transports = {};
// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
function addToPrefiltersOrTransports( structure ) {
// dataTypeExpression is optional and defaults to "*"
return function( dataTypeExpression, func ) {
if ( typeof dataTypeExpression !== "string" ) {
func = dataTypeExpression;
dataTypeExpression = "*";
}
if ( jQuery.isFunction( func ) ) {
var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),
i = 0,
length = dataTypes.length,
dataType,
list,
placeBefore;
// For each dataType in the dataTypeExpression
for(; i < length; i++ ) {
dataType = dataTypes[ i ];
// We control if we're asked to add before
// any existing element
placeBefore = /^\+/.test( dataType );
if ( placeBefore ) {
dataType = dataType.substr( 1 ) || "*";
}
list = structure[ dataType ] = structure[ dataType ] || [];
// then we add to the structure accordingly
list[ placeBefore ? "unshift" : "push" ]( func );
}
}
};
}
//Base inspection function for prefilters and transports
function inspectPrefiltersOrTransports( structure, options, originalOptions, jXHR,
dataType /* internal */, inspected /* internal */ ) {
dataType = dataType || options.dataTypes[ 0 ];
inspected = inspected || {};
inspected[ dataType ] = true;
var list = structure[ dataType ],
i = 0,
length = list ? list.length : 0,
executeOnly = ( structure === prefilters ),
selection;
for(; i < length && ( executeOnly || !selection ); i++ ) {
selection = list[ i ]( options, originalOptions, jXHR );
// If we got redirected to another dataType
// we try there if not done already
if ( typeof selection === "string" ) {
if ( inspected[ selection ] ) {
selection = undefined;
} else {
options.dataTypes.unshift( selection );
selection = inspectPrefiltersOrTransports(
structure, options, originalOptions, jXHR, selection, inspected );
}
}
}
// If we're only executing or nothing was selected
// we try the catchall dataType if not done already
if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
selection = inspectPrefiltersOrTransports(
structure, options, originalOptions, jXHR, "*", inspected );
}
// unnecessary when only executing (prefilters)
// but it'll be ignored by the caller in that case
return selection;
}
jQuery.fn.extend({
load: function( url, params, callback ) {
if ( typeof url !== "string" && _load ) {
return _load.apply( this, arguments );
// Don't do a request if no elements are being requested
} else if ( !this.length ) {
return this;
}
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, jQuery.ajaxSettings.traditional );
type = "POST";
}
}
var self = this;
// Request the remote document
jQuery.ajax({
url: url,
type: type,
dataType: "html",
data: params,
// Complete callback (responseText is used internally)
complete: function( jXHR, status, responseText ) {
// Store the response as specified by the jXHR object
responseText = jXHR.responseText;
// If successful, inject the HTML into all the matched elements
if ( jXHR.isResolved() ) {
// #4825: Get the actual response in case
// a dataFilter is present in ajaxSettings
jXHR.done(function( r ) {
responseText = r;
});
// 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(responseText.replace(rscript, ""))
// Locate the specified elements
.find(selector) :
// If not, just inject the full result
responseText );
}
if ( callback ) {
self.each( callback, [ responseText, status, jXHR ] );
}
}
});
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 || rselectTextarea.test( this.nodeName ) ||
rinput.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.replace( rCRLF, "\r\n" ) };
}) :
{ name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
}).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 );
};
} );
jQuery.each( [ "get", "post" ], function( i, method ) {
jQuery[ method ] = function( url, data, callback, type ) {
// shift arguments if data argument was omitted
if ( jQuery.isFunction( data ) ) {
type = type || callback;
callback = data;
data = null;
}
return jQuery.ajax({
type: method,
url: url,
data: data,
success: callback,
dataType: type
});
};
} );
jQuery.extend({
getScript: function( url, callback ) {
return jQuery.get( url, null, callback, "script" );
},
getJSON: function( url, data, callback ) {
return jQuery.get( url, data, callback, "json" );
},
ajaxSetup: function( settings ) {
jQuery.extend( true, jQuery.ajaxSettings, settings );
if ( settings.context ) {
jQuery.ajaxSettings.context = settings.context;
}
},
ajaxSettings: {
url: location.href,
global: true,
type: "GET",
contentType: "application/x-www-form-urlencoded",
processData: true,
async: true,
/*
timeout: 0,
data: null,
dataType: null,
username: null,
password: null,
cache: null,
traditional: false,
headers: {},
crossDomain: null,
*/
accepts: {
xml: "application/xml, text/xml",
html: "text/html",
text: "text/plain",
json: "application/json, text/javascript",
"*": "*/*"
},
contents: {
xml: /xml/,
html: /html/,
json: /json/
},
responseFields: {
xml: "responseXML",
text: "responseText"
},
// List of data converters
// 1) key format is "source_type destination_type" (a single space in-between)
// 2) the catchall symbol "*" can be used for source_type
converters: {
// Convert anything to text
"* text": window.String,
// Text to html (true = no transformation)
"text html": true,
// Evaluate text as a json expression
"text json": jQuery.parseJSON,
// Parse text as xml
"text xml": jQuery.parseXML
}
},
ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
ajaxTransport: addToPrefiltersOrTransports( transports ),
// Main method
ajax: function( url, options ) {
// If options is not an object,
// we simulate pre-1.5 signature
if ( typeof options !== "object" ) {
options = url;
url = undefined;
}
// Force options to be an object
options = options || {};
var // Create the final options object
s = jQuery.extend( true, {}, jQuery.ajaxSettings, options ),
// Callbacks contexts
// We force the original context if it exists
// or take it from jQuery.ajaxSettings otherwise
// (plain objects used as context get extended)
callbackContext =
( s.context = ( "context" in options ? options : jQuery.ajaxSettings ).context ) || s,
globalEventContext = callbackContext === s ? jQuery.event : jQuery( callbackContext ),
// Deferreds
deferred = jQuery.Deferred(),
completeDeferred = jQuery._Deferred(),
// Status-dependent callbacks
statusCode = s.statusCode || {},
// Headers (they are sent all at once)
requestHeaders = {},
// Response headers
responseHeadersString,
responseHeaders,
// transport
transport,
// timeout handle
timeoutTimer,
// Cross-domain detection vars
loc = document.location,
protocol = loc.protocol || "http:",
parts,
// The jXHR state
state = 0,
// Loop variable
i,
// Fake xhr
jXHR = {
readyState: 0,
// Caches the header
setRequestHeader: function( name, value ) {
if ( state === 0 ) {
requestHeaders[ name.toLowerCase() ] = value;
}
return this;
},
// Raw string
getAllResponseHeaders: function() {
return state === 2 ? responseHeadersString : null;
},
// Builds headers hashtable if needed
getResponseHeader: function( key ) {
var match;
if ( state === 2 ) {
if ( !responseHeaders ) {
responseHeaders = {};
while( ( match = rheaders.exec( responseHeadersString ) ) ) {
responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
}
}
match = responseHeaders[ key.toLowerCase() ];
}
return match || null;
},
// Cancel the request
abort: function( statusText ) {
statusText = statusText || "abort";
if ( transport ) {
transport.abort( statusText );
}
done( 0, statusText );
return this;
}
};
// Callback for when everything is done
// It is defined here because jslint complains if it is declared
// at the end of the function (which would be more logical and readable)
function done( status, statusText, responses, headers) {
// Called once
if ( state === 2 ) {
return;
}
// State is "done" now
state = 2;
// Clear timeout if it exists
if ( timeoutTimer ) {
clearTimeout( timeoutTimer );
}
// Dereference transport for early garbage collection
// (no matter how long the jXHR object will be used)
transport = undefined;
// Cache response headers
responseHeadersString = headers || "";
// Set readyState
jXHR.readyState = status ? 4 : 0;
var isSuccess,
success,
error,
response = responses ? ajaxHandleResponses( s, jXHR, responses ) : undefined,
lastModified,
etag;
// If successful, handle type chaining
if ( status >= 200 && status < 300 || status === 304 ) {
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
if ( s.ifModified ) {
if ( ( lastModified = jXHR.getResponseHeader( "Last-Modified" ) ) ) {
jQuery.lastModified[ s.url ] = lastModified;
}
if ( ( etag = jXHR.getResponseHeader( "Etag" ) ) ) {
jQuery.etag[ s.url ] = etag;
}
}
// If not modified
if ( status === 304 ) {
statusText = "notmodified";
isSuccess = true;
// If we have data
} else {
try {
success = ajaxConvert( s, response );
statusText = "success";
isSuccess = true;
} catch(e) {
// We have a parsererror
statusText = "parsererror";
error = e;
}
}
} else {
// We extract error from statusText
// then normalize statusText and status for non-aborts
error = statusText;
if( status ) {
statusText = "error";
if ( status < 0 ) {
status = 0;
}
}
}
// Set data for the fake xhr object
jXHR.status = status;
jXHR.statusText = statusText;
// Success/Error
if ( isSuccess ) {
deferred.resolveWith( callbackContext, [ success, statusText, jXHR ] );
} else {
deferred.rejectWith( callbackContext, [ jXHR, statusText, error ] );
}
// Status-dependent callbacks
jXHR.statusCode( statusCode );
statusCode = undefined;
if ( s.global ) {
globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
[ jXHR, s, isSuccess ? success : error ] );
}
// Complete
completeDeferred.resolveWith( callbackContext, [ jXHR, statusText ] );
if ( s.global ) {
globalEventContext.trigger( "ajaxComplete", [ jXHR, s] );
// Handle the global AJAX counter
if ( !( --jQuery.active ) ) {
jQuery.event.trigger( "ajaxStop" );
}
}
}
// Attach deferreds
deferred.promise( jXHR );
jXHR.success = jXHR.done;
jXHR.error = jXHR.fail;
jXHR.complete = completeDeferred.done;
// Status-dependent callbacks
jXHR.statusCode = function( map ) {
if ( map ) {
var tmp;
if ( state < 2 ) {
for( tmp in map ) {
statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
}
} else {
tmp = map[ jXHR.status ];
jXHR.then( tmp, tmp );
}
}
return this;
};
// Remove hash character (#7531: and string promotion)
// Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
// We also use the url parameter if available
s.url = ( "" + ( url || s.url ) ).replace( rhash, "" ).replace( rprotocol, protocol + "//" );
// Extract dataTypes list
s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax );
// Determine if a cross-domain request is in order
if ( !s.crossDomain ) {
parts = rurl.exec( s.url.toLowerCase() );
s.crossDomain = !!( parts &&
( parts[ 1 ] != protocol || parts[ 2 ] != loc.hostname ||
( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
( loc.port || ( protocol === "http:" ? 80 : 443 ) ) )
);
}
// Convert data if not already a string
if ( s.data && s.processData && typeof s.data !== "string" ) {
s.data = jQuery.param( s.data, s.traditional );
}
// Apply prefilters
inspectPrefiltersOrTransports( prefilters, s, options, jXHR );
// Uppercase the type
s.type = s.type.toUpperCase();
// Determine if request has content
s.hasContent = !rnoContent.test( s.type );
// Watch for a new set of requests
if ( s.global && jQuery.active++ === 0 ) {
jQuery.event.trigger( "ajaxStart" );
}
// More options handling for requests with no content
if ( !s.hasContent ) {
// If data is available, append data to url
if ( s.data ) {
s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
}
// Add anti-cache in url if needed
if ( s.cache === false ) {
var ts = jQuery.now(),
// try replacing _= if it is there
ret = s.url.replace( rts, "$1_=" + ts );
// if nothing was replaced, add timestamp to the end
s.url = ret + ( (ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
}
}
// Set the correct header, if data is being sent
if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
requestHeaders[ "content-type" ] = s.contentType;
}
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
if ( s.ifModified ) {
if ( jQuery.lastModified[ s.url ] ) {
requestHeaders[ "if-modified-since" ] = jQuery.lastModified[ s.url ];
}
if ( jQuery.etag[ s.url ] ) {
requestHeaders[ "if-none-match" ] = jQuery.etag[ s.url ];
}
}
// Set the Accepts header for the server, depending on the dataType
requestHeaders.accept = s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", */*; q=0.01" : "" ) :
s.accepts[ "*" ];
// Check for headers option
for ( i in s.headers ) {
requestHeaders[ i.toLowerCase() ] = s.headers[ i ];
}
// Allow custom headers/mimetypes and early abort
if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jXHR, s ) === false || state === 2 ) ) {
// Abort if not done already
done( 0, "abort" );
// Return false
jXHR = false;
} else {
// Install callbacks on deferreds
for ( i in { success: 1, error: 1, complete: 1 } ) {
jXHR[ i ]( s[ i ] );
}
// Get transport
transport = inspectPrefiltersOrTransports( transports, s, options, jXHR );
// If no transport, we auto-abort
if ( !transport ) {
done( -1, "No Transport" );
} else {
// Set state as sending
state = jXHR.readyState = 1;
// Send global event
if ( s.global ) {
globalEventContext.trigger( "ajaxSend", [ jXHR, s ] );
}
// Timeout
if ( s.async && s.timeout > 0 ) {
timeoutTimer = setTimeout( function(){
jXHR.abort( "timeout" );
}, s.timeout );
}
try {
transport.send( requestHeaders, done );
} catch (e) {
// Propagate exception as error if not done
if ( status < 2 ) {
done( -1, e );
// Simply rethrow otherwise
} else {
jQuery.error( e );
}
}
}
}
return jXHR;
},
// Serialize an array of form elements or a set of
// key/values into a query string
param: function( a, traditional ) {
var s = [],
add = function( key, value ) {
// If value is a function, invoke it and return its value
value = jQuery.isFunction( value ) ? value() : value;
s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
};
// Set traditional to true for jQuery <= 1.3.2 behavior.
if ( traditional === undefined ) {
traditional = jQuery.ajaxSettings.traditional;
}
// 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 );
} );
} else {
// If traditional, encode the "old" way (the way 1.3.2 or older
// did it), otherwise encode params recursively.
for ( var prefix in a ) {
buildParams( prefix, a[ prefix ], traditional, add );
}
}
// Return the resulting serialization
return s.join( "&" ).replace( r20, "+" );
}
});
function buildParams( prefix, obj, traditional, add ) {
if ( jQuery.isArray( obj ) && obj.length ) {
// Serialize array item.
jQuery.each( obj, function( i, v ) {
if ( traditional || rbracket.test( prefix ) ) {
// Treat each array item as a scalar.
add( prefix, v );
} else {
// If array item is non-scalar (array or object), encode its
// numeric index to resolve deserialization ambiguity issues.
// Note that rack (as of 1.0.0) can't currently deserialize
// nested arrays properly, and attempting to do so may cause
// a server error. Possible fixes are to modify rack's
// deserialization algorithm or to provide an option or flag
// to force array serialization to be shallow.
buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v, traditional, add );
}
});
} else if ( !traditional && obj != null && typeof obj === "object" ) {
// If we see an array here, it is empty and should be treated as an empty
// object
if ( jQuery.isArray( obj ) || jQuery.isEmptyObject( obj ) ) {
add( prefix, "" );
// Serialize object item.
} else {
jQuery.each( obj, function( k, v ) {
buildParams( prefix + "[" + k + "]", v, traditional, add );
});
}
} else {
// Serialize scalar item.
add( prefix, obj );
}
}
// This is still on the jQuery object... for now
// Want to move this to jQuery.ajax some day
jQuery.extend({
// Counter for holding the number of active queries
active: 0,
// Last-Modified header cache for next request
lastModified: {},
etag: {}
});
/* Handles responses to an ajax request:
* - sets all responseXXX fields accordingly
* - finds the right dataType (mediates between content-type and expected dataType)
* - returns the corresponding response
*/
function ajaxHandleResponses( s, jXHR, responses ) {
var contents = s.contents,
dataTypes = s.dataTypes,
responseFields = s.responseFields,
ct,
type,
finalDataType,
firstDataType;
// Fill responseXXX fields
for( type in responseFields ) {
if ( type in responses ) {
jXHR[ responseFields[type] ] = responses[ type ];
}
}
// Remove auto dataType and get content-type in the process
while( dataTypes[ 0 ] === "*" ) {
dataTypes.shift();
if ( ct === undefined ) {
ct = jXHR.getResponseHeader( "content-type" );
}
}
// Check if we're dealing with a known content-type
if ( ct ) {
for ( type in contents ) {
if ( contents[ type ] && contents[ type ].test( ct ) ) {
dataTypes.unshift( type );
break;
}
}
}
// Check to see if we have a response for the expected dataType
if ( dataTypes[ 0 ] in responses ) {
finalDataType = dataTypes[ 0 ];
} else {
// Try convertible dataTypes
for ( type in responses ) {
if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
finalDataType = type;
break;
}
if ( !firstDataType ) {
firstDataType = type;
}
}
// Or just use first one
finalDataType = finalDataType || firstDataType;
}
// If we found a dataType
// We add the dataType to the list if needed
// and return the corresponding response
if ( finalDataType ) {
if ( finalDataType !== dataTypes[ 0 ] ) {
dataTypes.unshift( finalDataType );
}
return responses[ finalDataType ];
}
}
// Chain conversions given the request and the original response
function ajaxConvert( s, response ) {
// Apply the dataFilter if provided
if ( s.dataFilter ) {
response = s.dataFilter( response, s.dataType );
}
var dataTypes = s.dataTypes,
converters = s.converters,
i,
length = dataTypes.length,
tmp,
// Current and previous dataTypes
current = dataTypes[ 0 ],
prev,
// Conversion expression
conversion,
// Conversion function
conv,
// Conversion functions (transitive conversion)
conv1,
conv2;
// For each dataType in the chain
for( i = 1; i < length; i++ ) {
// Get the dataTypes
prev = current;
current = dataTypes[ i ];
// If current is auto dataType, update it to prev
if( current === "*" ) {
current = prev;
// If no auto and dataTypes are actually different
} else if ( prev !== "*" && prev !== current ) {
// Get the converter
conversion = prev + " " + current;
conv = converters[ conversion ] || converters[ "* " + current ];
// If there is no direct converter, search transitively
if ( !conv ) {
conv2 = undefined;
for( conv1 in converters ) {
tmp = conv1.split( " " );
if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
conv2 = converters[ tmp[1] + " " + current ];
if ( conv2 ) {
conv1 = converters[ conv1 ];
if ( conv1 === true ) {
conv = conv2;
} else if ( conv2 === true ) {
conv = conv1;
}
break;
}
}
}
}
// If we found no converter, dispatch an error
if ( !( conv || conv2 ) ) {
jQuery.error( "No conversion from " + conversion.replace(" "," to ") );
}
// If found converter is not an equivalence
if ( conv !== true ) {
// Convert with 1 or 2 converters accordingly
response = conv ? conv( response ) : conv2( conv1(response) );
}
}
}
return response;
}
var jsc = jQuery.now(),
jsre = /(\=)\?(&|$)|()\?\?()/i;
// Default jsonp settings
jQuery.ajaxSetup({
jsonp: "callback",
jsonpCallback: function() {
return jQuery.expando + "_" + ( jsc++ );
}
});
// Detect, normalize options and install callbacks for jsonp requests
jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, dataIsString /* internal */ ) {
dataIsString = ( typeof s.data === "string" );
if ( s.dataTypes[ 0 ] === "jsonp" ||
originalSettings.jsonpCallback ||
originalSettings.jsonp != null ||
s.jsonp !== false && ( jsre.test( s.url ) ||
dataIsString && jsre.test( s.data ) ) ) {
var responseContainer,
jsonpCallback = s.jsonpCallback =
jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
previous = window[ jsonpCallback ],
url = s.url,
data = s.data,
replace = "$1" + jsonpCallback + "$2";
if ( s.jsonp !== false ) {
url = url.replace( jsre, replace );
if ( s.url === url ) {
if ( dataIsString ) {
data = data.replace( jsre, replace );
}
if ( s.data === data ) {
// Add callback manually
url += (/\?/.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback;
}
}
}
s.url = url;
s.data = data;
window[ jsonpCallback ] = function( response ) {
responseContainer = [ response ];
};
s.complete = [ function() {
// Set callback back to previous value
window[ jsonpCallback ] = previous;
// Call if it was a function and we have a response
if ( previous) {
if ( responseContainer && jQuery.isFunction( previous ) ) {
window[ jsonpCallback ] ( responseContainer[ 0 ] );
}
} else {
// else, more memory leak avoidance
try{
delete window[ jsonpCallback ];
} catch( e ) {}
}
}, s.complete ];
// Use data converter to retrieve json after script execution
s.converters["script json"] = function() {
if ( ! responseContainer ) {
jQuery.error( jsonpCallback + " was not called" );
}
return responseContainer[ 0 ];
};
// force json dataType
s.dataTypes[ 0 ] = "json";
// Delegate to script
return "script";
}
} );
// Install script dataType
jQuery.ajaxSetup({
accepts: {
script: "text/javascript, application/javascript"
},
contents: {
script: /javascript/
},
converters: {
"text script": function( text ) {
jQuery.globalEval( text );
return text;
}
}
});
// Handle cache's special case and global
jQuery.ajaxPrefilter( "script", function( s ) {
if ( s.cache === undefined ) {
s.cache = false;
}
if ( s.crossDomain ) {
s.type = "GET";
s.global = false;
}
} );
// Bind script tag hack transport
jQuery.ajaxTransport( "script", function(s) {
// This transport only deals with cross domain requests
if ( s.crossDomain ) {
var script,
head = document.getElementsByTagName( "head" )[ 0 ] || document.documentElement;
return {
send: function( _, callback ) {
script = document.createElement( "script" );
script.async = "async";
if ( s.scriptCharset ) {
script.charset = s.scriptCharset;
}
script.src = s.url;
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function( _, isAbort ) {
if ( !script.readyState || /loaded|complete/.test( script.readyState ) ) {
// Handle memory leak in IE
script.onload = script.onreadystatechange = null;
// Remove the script
if ( head && script.parentNode ) {
head.removeChild( script );
}
// Dereference the script
script = undefined;
// Callback if not abort
if ( !isAbort ) {
callback( 200, "success" );
}
}
};
// Use insertBefore instead of appendChild to circumvent an IE6 bug.
// This arises when a base node is used (#2709 and #4378).
head.insertBefore( script, head.firstChild );
},
abort: function() {
if ( script ) {
script.onload( 0, 1 );
}
}
};
}
} );
var // Next active xhr id
xhrId = jQuery.now(),
// active xhrs
xhrs = {},
// #5280: see below
xhrUnloadAbortInstalled,
// XHR used to determine supports properties
testXHR;
// Create the request object
// (This is still attached to ajaxSettings for backward compatibility)
jQuery.ajaxSettings.xhr = window.ActiveXObject ?
/* Microsoft failed to properly
* implement the XMLHttpRequest in IE7 (can't request local files),
* so we use the ActiveXObject when it is available
* Additionally XMLHttpRequest can be disabled in IE7/IE8 so
* we need a fallback.
*/
function() {
if ( window.location.protocol !== "file:" ) {
try {
return new window.XMLHttpRequest();
} catch( xhrError ) {}
}
try {
return new window.ActiveXObject("Microsoft.XMLHTTP");
} catch( activeError ) {}
} :
// For all other browsers, use the standard XMLHttpRequest object
function() {
return new window.XMLHttpRequest();
};
// Test if we can create an xhr object
try {
testXHR = jQuery.ajaxSettings.xhr();
} catch( xhrCreationException ) {}
//Does this browser support XHR requests?
jQuery.support.ajax = !!testXHR;
// Does this browser support crossDomain XHR requests
jQuery.support.cors = testXHR && ( "withCredentials" in testXHR );
// No need for the temporary xhr anymore
testXHR = undefined;
// Create transport if the browser can provide an xhr
if ( jQuery.support.ajax ) {
jQuery.ajaxTransport(function( s ) {
// Cross domain only allowed if supported through XMLHttpRequest
if ( !s.crossDomain || jQuery.support.cors ) {
var callback;
return {
send: function( headers, complete ) {
// #5280: we need to abort on unload or IE will keep connections alive
if ( !xhrUnloadAbortInstalled ) {
xhrUnloadAbortInstalled = 1;
jQuery(window).bind( "unload", function() {
// Abort all pending requests
jQuery.each( xhrs, function( _, xhr ) {
if ( xhr.onreadystatechange ) {
xhr.onreadystatechange( 1 );
}
} );
} );
}
// Get a new xhr
var xhr = s.xhr(),
handle;
// Open the socket
// Passing null username, generates a login popup on Opera (#2865)
if ( s.username ) {
xhr.open( s.type, s.url, s.async, s.username, s.password );
} else {
xhr.open( s.type, s.url, s.async );
}
// Requested-With header
// Not set for crossDomain requests with no content
// (see why at http://trac.dojotoolkit.org/ticket/9486)
// Won't change header if already provided
if ( !( s.crossDomain && !s.hasContent ) && !headers["x-requested-with"] ) {
headers[ "x-requested-with" ] = "XMLHttpRequest";
}
// Need an extra try/catch for cross domain requests in Firefox 3
try {
jQuery.each( headers, function( key, value ) {
xhr.setRequestHeader( key, value );
} );
} catch( _ ) {}
// Do send the request
// This may raise an exception which is actually
// handled in jQuery.ajax (so no try/catch here)
xhr.send( ( s.hasContent && s.data ) || null );
// Listener
callback = function( _, isAbort ) {
// Was never called and is aborted or complete
if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
// Only called once
callback = 0;
// Do not keep as active anymore
if ( handle ) {
xhr.onreadystatechange = jQuery.noop;
delete xhrs[ handle ];
}
// If it's an abort
if ( isAbort ) {
// Abort it manually if needed
if ( xhr.readyState !== 4 ) {
xhr.abort();
}
} else {
// Get info
var status = xhr.status,
statusText,
responseHeaders = xhr.getAllResponseHeaders(),
responses = {},
xml = xhr.responseXML;
// Construct response list
if ( xml && xml.documentElement /* #4958 */ ) {
responses.xml = xml;
}
responses.text = xhr.responseText;
// Firefox throws an exception when accessing
// statusText for faulty cross-domain requests
try {
statusText = xhr.statusText;
} catch( e ) {
// We normalize with Webkit giving an empty statusText
statusText = "";
}
// Filter status for non standard behaviours
status =
// Opera returns 0 when it should be 304
// Webkit returns 0 for failing cross-domain no matter the real status
status === 0 ?
(
// Webkit, Firefox: filter out faulty cross-domain requests
!s.crossDomain || statusText ?
(
// Opera: filter out real aborts #6060
responseHeaders ?
304 :
0
) :
// We assume 302 but could be anything cross-domain related
302
) :
(
// IE sometimes returns 1223 when it should be 204 (see #1450)
status == 1223 ?
204 :
status
);
// Call complete
complete( status, statusText, responses, responseHeaders );
}
}
};
// if we're in sync mode or it's in cache
// and has been retrieved directly (IE6 & IE7)
// we need to manually fire the callback
if ( !s.async || xhr.readyState === 4 ) {
callback();
} else {
// Add to list of active xhrs
handle = xhrId++;
xhrs[ handle ] = xhr;
xhr.onreadystatechange = callback;
}
},
abort: function() {
if ( callback ) {
callback(0,1);
}
}
};
}
});
}
var elemdisplay = {},
rfxtypes = /^(?:toggle|show|hide)$/,
rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
timerId,
fxAttrs = [
// height animations
[ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
// width animations
[ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
// opacity animations
[ "opacity" ]
];
jQuery.fn.extend({
show: function( speed, easing, callback ) {
var elem, display;
if ( speed || speed === 0 ) {
return this.animate( genFx("show", 3), speed, easing, callback);
} else {
for ( var i = 0, j = this.length; i < j; i++ ) {
elem = this[i];
display = elem.style.display;
// Reset the inline display of this element to learn if it is
// being hidden by cascaded rules or not
if ( !jQuery._data(elem, "olddisplay") && display === "none" ) {
display = elem.style.display = "";
}
// Set elements which have been overridden with display: none
// in a stylesheet to whatever the default browser style is
// for such an element
if ( display === "" && jQuery.css( elem, "display" ) === "none" ) {
jQuery._data(elem, "olddisplay", defaultDisplay(elem.nodeName));
}
}
// Set the display of most of the elements in a second loop
// to avoid the constant reflow
for ( i = 0; i < j; i++ ) {
elem = this[i];
display = elem.style.display;
if ( display === "" || display === "none" ) {
elem.style.display = jQuery._data(elem, "olddisplay") || "";
}
}
return this;
}
},
hide: function( speed, easing, callback ) {
if ( speed || speed === 0 ) {
return this.animate( genFx("hide", 3), speed, easing, callback);
} else {
for ( var i = 0, j = this.length; i < j; i++ ) {
var display = jQuery.css( this[i], "display" );
if ( display !== "none" && !jQuery._data( this[i], "olddisplay" ) ) {
jQuery._data( this[i], "olddisplay", display );
}
}
// Set the display of the elements in a second loop
// to avoid the constant reflow
for ( i = 0; i < j; i++ ) {
this[i].style.display = "none";
}
return this;
}
},
// Save the old toggle function
_toggle: jQuery.fn.toggle,
toggle: function( fn, fn2, callback ) {
var bool = typeof fn === "boolean";
if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
this._toggle.apply( this, arguments );
} else if ( fn == null || bool ) {
this.each(function() {
var state = bool ? fn : jQuery(this).is(":hidden");
jQuery(this)[ state ? "show" : "hide" ]();
});
} else {
this.animate(genFx("toggle", 3), fn, fn2, callback);
}
return this;
},
fadeTo: function( speed, to, easing, callback ) {
return this.filter(":hidden").css("opacity", 0).show().end()
.animate({opacity: to}, speed, easing, callback);
},
animate: function( prop, speed, easing, callback ) {
var optall = jQuery.speed(speed, easing, callback);
if ( jQuery.isEmptyObject( prop ) ) {
return this.each( optall.complete );
}
return this[ optall.queue === false ? "each" : "queue" ](function() {
// XXX 'this' does not always have a nodeName when running the
// test suite
var opt = jQuery.extend({}, optall), p,
isElement = this.nodeType === 1,
hidden = isElement && jQuery(this).is(":hidden"),
self = this;
for ( p in prop ) {
var name = jQuery.camelCase( p );
if ( p !== name ) {
prop[ name ] = prop[ p ];
delete prop[ p ];
p = name;
}
if ( prop[p] === "hide" && hidden || prop[p] === "show" && !hidden ) {
return opt.complete.call(this);
}
if ( isElement && ( p === "height" || p === "width" ) ) {
// Make sure that nothing sneaks out
// Record all 3 overflow attributes because IE does not
// change the overflow attribute when overflowX and
// overflowY are set to the same value
opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];
// Set display property to inline-block for height/width
// animations on inline elements that are having width/height
// animated
if ( jQuery.css( this, "display" ) === "inline" &&
jQuery.css( this, "float" ) === "none" ) {
if ( !jQuery.support.inlineBlockNeedsLayout ) {
this.style.display = "inline-block";
} else {
var display = defaultDisplay(this.nodeName);
// inline-level elements accept inline-block;
// block-level elements need to be inline with layout
if ( display === "inline" ) {
this.style.display = "inline-block";
} else {
this.style.display = "inline";
this.style.zoom = 1;
}
}
}
}
if ( jQuery.isArray( prop[p] ) ) {
// Create (if needed) and add to specialEasing
(opt.specialEasing = opt.specialEasing || {})[p] = prop[p][1];
prop[p] = prop[p][0];
}
}
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 ( rfxtypes.test(val) ) {
e[ val === "toggle" ? hidden ? "show" : "hide" : val ]( prop );
} else {
var parts = rfxnum.exec(val),
start = e.cur() || 0;
if ( parts ) {
var end = parseFloat( parts[2] ),
unit = parts[3] || "px";
// We need to compute starting value
if ( unit !== "px" ) {
jQuery.style( self, name, (end || 1) + unit);
start = ((end || 1) / e.cur()) * start;
jQuery.style( self, 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;
}
});
function genFx( type, num ) {
var obj = {};
jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {
obj[ this ] = type;
});
return obj;
}
// 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" },
fadeToggle: { opacity: "toggle" }
}, function( name, props ) {
jQuery.fn[ name ] = function( speed, easing, callback ) {
return this.animate( props, speed, easing, callback );
};
});
jQuery.extend({
speed: function( speed, easing, fn ) {
var opt = speed && typeof speed === "object" ? jQuery.extend({}, 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 :
opt.duration in jQuery.fx.speeds ? 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 );
},
// Get the current size
cur: function() {
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 ) );
return r || 0;
},
// Start an animation from one number to another
custom: function( from, to, unit ) {
var self = this,
fx = jQuery.fx;
this.startTime = jQuery.now();
this.start = from;
this.end = to;
this.unit = unit || this.unit || "px";
this.now = this.start;
this.pos = this.state = 0;
function t( gotoEnd ) {
return self.step(gotoEnd);
}
t.elem = this.elem;
if ( t() && jQuery.timers.push(t) && !timerId ) {
timerId = setInterval(fx.tick, fx.interval);
}
},
// Simple 'show' function
show: function() {
// Remember where we started, so that we can go back to it later
this.options.orig[this.prop] = jQuery.style( this.elem, 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.style( this.elem, this.prop );
this.options.hide = true;
// Begin the animation
this.custom(this.cur(), 0);
},
// Each step of an animation
step: function( gotoEnd ) {
var t = jQuery.now(), done = true;
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;
for ( var i in this.options.curAnim ) {
if ( this.options.curAnim[i] !== true ) {
done = false;
}
}
if ( done ) {
// Reset the overflow
if ( this.options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
var elem = this.elem,
options = this.options;
jQuery.each( [ "", "X", "Y" ], function (index, value) {
elem.style[ "overflow" + value ] = options.overflow[index];
} );
}
// 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.style( this.elem, 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
var specialEasing = this.options.specialEasing && this.options.specialEasing[this.prop];
var defaultEasing = this.options.easing || (jQuery.easing.swing ? "swing" : "linear");
this.pos = jQuery.easing[specialEasing || defaultEasing](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, {
tick: function() {
var timers = jQuery.timers;
for ( var i = 0; i < timers.length; i++ ) {
if ( !timers[i]() ) {
timers.splice(i--, 1);
}
}
if ( !timers.length ) {
jQuery.fx.stop();
}
},
interval: 13,
stop: function() {
clearInterval( timerId );
timerId = null;
},
speeds: {
slow: 600,
fast: 200,
// Default speed
_default: 400
},
step: {
opacity: function( fx ) {
jQuery.style( fx.elem, "opacity", fx.now );
},
_default: function( fx ) {
if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit;
} else {
fx.elem[ fx.prop ] = fx.now;
}
}
}
});
if ( jQuery.expr && jQuery.expr.filters ) {
jQuery.expr.filters.animated = function( elem ) {
return jQuery.grep(jQuery.timers, function( fn ) {
return elem === fn.elem;
}).length;
};
}
function defaultDisplay( nodeName ) {
if ( !elemdisplay[ nodeName ] ) {
var elem = jQuery("<" + nodeName + ">").appendTo("body"),
display = elem.css("display");
elem.remove();
if ( display === "none" || display === "" ) {
display = "block";
}
elemdisplay[ nodeName ] = display;
}
return elemdisplay[ nodeName ];
}
var rtable = /^t(?:able|d|h)$/i,
rroot = /^(?:body|html)$/i;
if ( "getBoundingClientRect" in document.documentElement ) {
jQuery.fn.offset = function( options ) {
var elem = this[0], box;
if ( options ) {
return this.each(function( i ) {
jQuery.offset.setOffset( this, options, i );
});
}
if ( !elem || !elem.ownerDocument ) {
return null;
}
if ( elem === elem.ownerDocument.body ) {
return jQuery.offset.bodyOffset( elem );
}
try {
box = elem.getBoundingClientRect();
} catch(e) {}
var doc = elem.ownerDocument,
docElem = doc.documentElement;
// Make sure we're not dealing with a disconnected DOM node
if ( !box || !jQuery.contains( docElem, elem ) ) {
return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
}
var body = doc.body,
win = getWindow(doc),
clientTop = docElem.clientTop || body.clientTop || 0,
clientLeft = docElem.clientLeft || body.clientLeft || 0,
scrollTop = (win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop ),
scrollLeft = (win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft),
top = box.top + scrollTop - clientTop,
left = box.left + scrollLeft - clientLeft;
return { top: top, left: left };
};
} else {
jQuery.fn.offset = function( options ) {
var elem = this[0];
if ( options ) {
return this.each(function( i ) {
jQuery.offset.setOffset( this, options, i );
});
}
if ( !elem || !elem.ownerDocument ) {
return null;
}
if ( elem === elem.ownerDocument.body ) {
return jQuery.offset.bodyOffset( elem );
}
jQuery.offset.initialize();
var computedStyle,
offsetParent = elem.offsetParent,
prevOffsetParent = elem,
doc = elem.ownerDocument,
docElem = doc.documentElement,
body = doc.body,
defaultView = doc.defaultView,
prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
top = elem.offsetTop,
left = elem.offsetLeft;
while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
break;
}
computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
top -= elem.scrollTop;
left -= elem.scrollLeft;
if ( elem === offsetParent ) {
top += elem.offsetTop;
left += elem.offsetLeft;
if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {
top += parseFloat( computedStyle.borderTopWidth ) || 0;
left += parseFloat( computedStyle.borderLeftWidth ) || 0;
}
prevOffsetParent = offsetParent;
offsetParent = elem.offsetParent;
}
if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
top += parseFloat( computedStyle.borderTopWidth ) || 0;
left += parseFloat( computedStyle.borderLeftWidth ) || 0;
}
prevComputedStyle = computedStyle;
}
if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
top += body.offsetTop;
left += body.offsetLeft;
}
if ( jQuery.offset.supportsFixedPosition && 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() {
var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.css(body, "marginTop") ) || 0,
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>";
jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );
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);
checkDiv.style.position = "fixed";
checkDiv.style.top = "20px";
// safari subtracts parent border width here which is 5px
this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);
checkDiv.style.position = checkDiv.style.top = "";
innerDiv.style.overflow = "hidden";
innerDiv.style.position = "relative";
this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);
body.removeChild( container );
body = container = innerDiv = checkDiv = table = td = null;
jQuery.offset.initialize = jQuery.noop;
},
bodyOffset: function( body ) {
var top = body.offsetTop,
left = body.offsetLeft;
jQuery.offset.initialize();
if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {
top += parseFloat( jQuery.css(body, "marginTop") ) || 0;
left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
}
return { top: top, left: left };
},
setOffset: function( elem, options, i ) {
var position = jQuery.css( elem, "position" );
// set position first, in-case top/left are set even on static elem
if ( position === "static" ) {
elem.style.position = "relative";
}
var curElem = jQuery( elem ),
curOffset = curElem.offset(),
curCSSTop = jQuery.css( elem, "top" ),
curCSSLeft = jQuery.css( elem, "left" ),
calculatePosition = (position === "absolute" && jQuery.inArray('auto', [curCSSTop, curCSSLeft]) > -1),
props = {}, curPosition = {}, curTop, curLeft;
// need to be able to calculate position if either top or left is auto and position is absolute
if ( calculatePosition ) {
curPosition = curElem.position();
}
curTop = calculatePosition ? curPosition.top : parseInt( curCSSTop, 10 ) || 0;
curLeft = calculatePosition ? curPosition.left : parseInt( curCSSLeft, 10 ) || 0;
if ( jQuery.isFunction( options ) ) {
options = options.call( elem, i, curOffset );
}
if (options.top != null) {
props.top = (options.top - curOffset.top) + curTop;
}
if (options.left != null) {
props.left = (options.left - curOffset.left) + curLeft;
}
if ( "using" in options ) {
options.using.call( elem, props );
} else {
curElem.css( props );
}
}
};
jQuery.fn.extend({
position: function() {
if ( !this[0] ) {
return null;
}
var elem = this[0],
// Get *real* offsetParent
offsetParent = this.offsetParent(),
// Get correct offsets
offset = this.offset(),
parentOffset = rroot.test(offsetParent[0].nodeName) ? { 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 -= parseFloat( jQuery.css(elem, "marginTop") ) || 0;
offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0;
// Add offsetParent borders
parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0;
parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0;
// Subtract the two offsets
return {
top: offset.top - parentOffset.top,
left: offset.left - parentOffset.left
};
},
offsetParent: function() {
return this.map(function() {
var offsetParent = this.offsetParent || document.body;
while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
offsetParent = offsetParent.offsetParent;
}
return offsetParent;
});
}
});
// Create scrollLeft and scrollTop methods
jQuery.each( ["Left", "Top"], function( i, name ) {
var method = "scroll" + name;
jQuery.fn[ method ] = function(val) {
var elem = this[0], win;
if ( !elem ) {
return null;
}
if ( val !== undefined ) {
// Set the scroll offset
return this.each(function() {
win = getWindow( this );
if ( win ) {
win.scrollTo(
!i ? val : jQuery(win).scrollLeft(),
i ? val : jQuery(win).scrollTop()
);
} else {
this[ method ] = val;
}
});
} else {
win = getWindow( elem );
// Return the scroll offset
return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] :
jQuery.support.boxModel && win.document.documentElement[ method ] ||
win.document.body[ method ] :
elem[ method ];
}
};
});
function getWindow( elem ) {
return jQuery.isWindow( elem ) ?
elem :
elem.nodeType === 9 ?
elem.defaultView || elem.parentWindow :
false;
}
// Create innerHeight, innerWidth, outerHeight and outerWidth methods
jQuery.each([ "Height", "Width" ], function( i, name ) {
var type = name.toLowerCase();
// innerHeight and innerWidth
jQuery.fn["inner" + name] = function() {
return this[0] ?
parseFloat( jQuery.css( this[0], type, "padding" ) ) :
null;
};
// outerHeight and outerWidth
jQuery.fn["outer" + name] = function( margin ) {
return this[0] ?
parseFloat( jQuery.css( this[0], type, margin ? "margin" : "border" ) ) :
null;
};
jQuery.fn[ type ] = function( size ) {
// Get window width or height
var elem = this[0];
if ( !elem ) {
return size == null ? null : this;
}
if ( jQuery.isFunction( size ) ) {
return this.each(function( i ) {
var self = jQuery( this );
self[ type ]( size.call( this, i, self[ type ]() ) );
});
}
if ( jQuery.isWindow( elem ) ) {
// Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
// 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat
var docElemProp = elem.document.documentElement[ "client" + name ];
return elem.document.compatMode === "CSS1Compat" && docElemProp ||
elem.document.body[ "client" + name ] || docElemProp;
// Get document width or height
} else if ( elem.nodeType === 9 ) {
// Either scroll[Width/Height] or offset[Width/Height], whichever is greater
return Math.max(
elem.documentElement["client" + name],
elem.body["scroll" + name], elem.documentElement["scroll" + name],
elem.body["offset" + name], elem.documentElement["offset" + name]
);
// Get or set width or height on the element
} else if ( size === undefined ) {
var orig = jQuery.css( elem, type ),
ret = parseFloat( orig );
return jQuery.isNaN( ret ) ? orig : ret;
// Set the width or height on the element (default to pixels if value is unitless)
} else {
return this.css( type, typeof size === "string" ? size : size + "px" );
}
};
});
})(window);

View File

@ -1,4332 +0,0 @@
/**
* @preserve jquery.layout 1.3.0 - Release Candidate 29.14
* $Date: 2011-02-13 08:00:00 (Sun, 13 Feb 2011) $
* $Rev: 302914 $
*
* Copyright (c) 2010
* 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.
*
* Changelog: http://layout.jquery-dev.net/changelog.cfm#1.3.0.rc29.13
*
* Docs: http://layout.jquery-dev.net/documentation.html
* Tips: http://layout.jquery-dev.net/tips.html
* Help: http://groups.google.com/group/jquery-ui-layout
*/
// NOTE: For best readability, view with a fixed-width font and tabs equal to 4-chars
;(function ($) {
var $b = $.browser;
/*
* GENERIC $.layout METHODS - used by all layouts
*/
$.layout = {
// can update code here if $.browser is phased out
browser: {
mozilla: !!$b.mozilla
, webkit: !!$b.webkit || !!$b.safari // webkit = jQ 1.4
, msie: !!$b.msie
, isIE6: !!$b.msie && $b.version == 6
, boxModel: false // page must load first, so will be updated set by _create
//, version: $b.version - not used
}
/*
* USER UTILITIES
*/
// calculate and return the scrollbar width, as an integer
, scrollbarWidth: function () { return window.scrollbarWidth || $.layout.getScrollbarSize('width'); }
, scrollbarHeight: function () { return window.scrollbarHeight || $.layout.getScrollbarSize('height'); }
, getScrollbarSize: function (dim) {
var $c = $('<div style="position: absolute; top: -10000px; left: -10000px; width: 100px; height: 100px; overflow: scroll;"></div>').appendTo("body");
var d = { width: $c.width() - $c[0].clientWidth, height: $c.height() - $c[0].clientHeight };
$c.remove();
window.scrollbarWidth = d.width;
window.scrollbarHeight = d.height;
return dim.match(/^(width|height)$/i) ? d[dim] : d;
}
/**
* Returns hash container 'display' and 'visibility'
*
* @see $.swap() - swaps CSS, runs callback, resets CSS
*/
, showInvisibly: function ($E, force) {
if (!$E) return {};
if (!$E.jquery) $E = $($E);
var CSS = {
display: $E.css('display')
, visibility: $E.css('visibility')
};
if (force || CSS.display == "none") { // only if not *already hidden*
$E.css({ display: "block", visibility: "hidden" }); // show element 'invisibly' so can be measured
return CSS;
}
else return {};
}
/**
* Returns data for setting size of an element (container or a pane).
*
* @see _create(), onWindowResize() for container, plus others for pane
* @return JSON Returns a hash of all dimensions: top, bottom, left, right, outerWidth, innerHeight, etc
*/
, getElemDims: function ($E) {
var
d = {} // dimensions hash
, x = d.css = {} // CSS hash
, i = {} // TEMP insets
, b, p // TEMP border, padding
, off = $E.offset()
;
d.offsetLeft = off.left;
d.offsetTop = off.top;
$.each("Left,Right,Top,Bottom".split(","), function (idx, e) { // e = edge
b = x["border" + e] = $.layout.borderWidth($E, e);
p = x["padding"+ e] = $.layout.cssNum($E, "padding"+e);
i[e] = b + p; // total offset of content from outer side
d["inset"+ e] = p;
/* WRONG ???
// if BOX MODEL, then 'position' = PADDING (ignore borderWidth)
if ($E == $Container)
d["inset"+ e] = (browser.boxModel ? p : 0);
*/
});
d.offsetWidth = $E.innerWidth();
d.offsetHeight = $E.innerHeight();
d.outerWidth = $E.outerWidth();
d.outerHeight = $E.outerHeight();
d.innerWidth = d.outerWidth - i.Left - i.Right;
d.innerHeight = d.outerHeight - i.Top - i.Bottom;
// TESTING
x.width = $E.width();
x.height = $E.height();
return d;
}
, getElemCSS: function ($E, list) {
var
CSS = {}
, style = $E[0].style
, props = list.split(",")
, sides = "Top,Bottom,Left,Right".split(",")
, attrs = "Color,Style,Width".split(",")
, p, s, a, i, j, k
;
for (i=0; i < props.length; i++) {
p = props[i];
if (p.match(/(border|padding|margin)$/))
for (j=0; j < 4; j++) {
s = sides[j];
if (p == "border")
for (k=0; k < 3; k++) {
a = attrs[k];
CSS[p+s+a] = style[p+s+a];
}
else
CSS[p+s] = style[p+s];
}
else
CSS[p] = style[p];
};
return CSS
}
/**
* Contains logic to check boxModel & browser, and return the correct width/height for the current browser/doctype
*
* @see initPanes(), sizeMidPanes(), initHandles(), sizeHandles()
* @param {Array.<Object>} $E Must pass a jQuery object - first element is processed
* @param {number=} outerWidth/outerHeight (optional) Can pass a width, allowing calculations BEFORE element is resized
* @return {number} Returns the innerWidth/Height of the elem by subtracting padding and borders
*/
, cssWidth: function ($E, outerWidth) {
var
b = $.layout.borderWidth
, n = $.layout.cssNum
;
// a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
if (outerWidth <= 0) return 0;
if (!$.layout.browser.boxModel) return outerWidth;
// strip border and padding from outerWidth to get CSS Width
var W = outerWidth
- b($E, "Left")
- b($E, "Right")
- n($E, "paddingLeft")
- n($E, "paddingRight")
;
return Math.max(0,W);
}
, cssHeight: function ($E, outerHeight) {
var
b = $.layout.borderWidth
, n = $.layout.cssNum
;
// a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
if (outerHeight <= 0) return 0;
if (!$.layout.browser.boxModel) return outerHeight;
// strip border and padding from outerHeight to get CSS Height
var H = outerHeight
- b($E, "Top")
- b($E, "Bottom")
- n($E, "paddingTop")
- n($E, "paddingBottom")
;
return Math.max(0,H);
}
/**
* Returns the 'current CSS numeric value' for an element - returns 0 if property does not exist
*
* @see Called by many methods
* @param {Array.<Object>} $E Must pass a jQuery object - first element is processed
* @param {string} prop The name of the CSS property, eg: top, width, etc.
* @return {*} Usually is used to get an integer value for position (top, left) or size (height, width)
*/
, cssNum: function ($E, prop) {
if (!$E.jquery) $E = $($E);
var CSS = $.layout.showInvisibly($E);
var val = parseInt($.curCSS($E[0], prop, true), 10) || 0;
$E.css( CSS ); // RESET
return val;
}
, borderWidth: function (el, side) {
if (el.jquery) el = el[0];
var b = "border"+ side.substr(0,1).toUpperCase() + side.substr(1); // left => Left
return $.curCSS(el, b+"Style", true) == "none" ? 0 : (parseInt($.curCSS(el, b+"Width", true), 10) || 0);
}
/**
* SUBROUTINE for preventPrematureSlideClose option
*
* @param {Object} evt
* @param {Object=} el
*/
, isMouseOverElem: function (evt, el) {
var
$E = $(el || this)
, d = $E.offset()
, T = d.top
, L = d.left
, R = L + $E.outerWidth()
, B = T + $E.outerHeight()
, x = evt.pageX
, y = evt.pageY
;
// if X & Y are < 0, probably means is over an open SELECT
return ($.layout.browser.msie && x < 0 && y < 0) || ((x >= L && x <= R) && (y >= T && y <= B));
}
};
$.fn.layout = function (opts) {
/*
* ###########################
* WIDGET CONFIG & OPTIONS
* ###########################
*/
// LANGUAGE CUSTOMIZATION - will be *externally customizable* in next version
var lang = {
Pane: "Pane"
, Open: "Open" // eg: "Open Pane"
, Close: "Close"
, Resize: "Resize"
, Slide: "Slide Open"
, Pin: "Pin"
, Unpin: "Un-Pin"
, selector: "selector"
, msgNoRoom: "Not enough room to show this pane."
, errContainerMissing: "UI Layout Initialization Error\n\nThe specified layout-container does not exist."
, errCenterPaneMissing: "UI Layout Initialization Error\n\nThe center-pane element does not exist.\n\nThe center-pane is a required element."
, errContainerHeight: "UI Layout Initialization Warning\n\nThe layout-container \"CONTAINER\" has no height.\n\nTherefore the layout is 0-height and hence 'invisible'!"
, errButton: "Error Adding Button \n\nInvalid "
};
// DEFAULT OPTIONS - CHANGE IF DESIRED
var options = {
name: "" // Not required, but useful for buttons and used for the state-cookie
, containerClass: "ui-layout-container" // layout-container element
, scrollToBookmarkOnLoad: true // after creating a layout, scroll to bookmark in URL (.../page.htm#myBookmark)
, resizeWithWindow: true // bind thisLayout.resizeAll() to the window.resize event
, resizeWithWindowDelay: 200 // delay calling resizeAll because makes window resizing very jerky
, resizeWithWindowMaxDelay: 0 // 0 = none - force resize every XX ms while window is being resized
, onresizeall_start: null // CALLBACK when resizeAll() STARTS - NOT pane-specific
, onresizeall_end: null // CALLBACK when resizeAll() ENDS - NOT pane-specific
, onload_start: null // CALLBACK when Layout inits - after options initialized, but before elements
, onload_end: null // CALLBACK when Layout inits - after EVERYTHING has been initialized
, onunload_start: null // CALLBACK when Layout is destroyed OR onWindowUnload
, onunload_end: null // CALLBACK when Layout is destroyed OR onWindowUnload
, autoBindCustomButtons: false // search for buttons with ui-layout-button class and auto-bind them
, zIndex: null // the PANE zIndex - resizers and masks will be +1
// PANE SETTINGS
, defaults: { // default options for 'all panes' - will be overridden by 'per-pane settings'
applyDemoStyles: false // NOTE: renamed from applyDefaultStyles for clarity
, 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
, initClosed: false // true = init pane as 'closed'
, initHidden: false // true = init pane as 'hidden' - no resizer-bar/spacing
// SELECTORS
//, paneSelector: "" // MUST be pane-specific - jQuery selector for pane
, contentSelector: ".ui-layout-content" // INNER div/element to auto-size so only it scrolls, not the entire pane!
, contentIgnoreSelector: ".ui-layout-ignore" // element(s) to 'ignore' when measuring 'content'
, findNestedContent: false // true = $P.find(contentSelector), false = $P.children(contentSelector)
// GENERIC ROOT-CLASSES - for auto-generated classNames
, paneClass: "ui-layout-pane" // border-Pane - default: 'ui-layout-pane'
, resizerClass: "ui-layout-resizer" // Resizer Bar - default: 'ui-layout-resizer'
, togglerClass: "ui-layout-toggler" // Toggler Button - default: 'ui-layout-toggler'
, buttonClass: "ui-layout-button" // CUSTOM Buttons - default: 'ui-layout-button-toggle/-open/-close/-pin'
// ELEMENT SIZE & SPACING
//, size: 100 // MUST be pane-specific -initial size of 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 sides - HEIGHT on east/west sides
, 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: lang.Close // Toggler tool-tip (title)
, togglerTip_closed: lang.Open // ditto
, togglerContent_open: "" // text or HTML to put INSIDE the toggler
, togglerContent_closed: "" // ditto
// RESIZING OPTIONS
, resizerDblClickToggle: true //
, autoResize: true // IF size is 'auto' or a percentage, then recalc 'pixel size' whenever the layout resizes
, autoReopen: true // IF a pane was auto-closed due to noRoom, reopen it when there is room? False = leave it closed
, 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
, resizeNestedLayout: true // true = trigger nested.resizeAll() when a 'pane' of this layout is the 'container' for another
, resizeWhileDragging: false // true = LIVE Resizing as resizer is dragged
, resizeContentWhileDragging: false // true = re-measure header/footer heights as resizer is dragged
// TIPS & MESSAGES - also see lang object
, noRoomToOpenTip: lang.msgNoRoom
, resizerTip: lang.Resize // Resizer tool-tip (title)
, sliderTip: lang.Slide // resizer-bar triggers 'sliding' when pane is closed
, sliderCursor: "pointer" // cursor when resizer-bar will trigger 'sliding'
, slideTrigger_open: "click" // click, dblclick, mouseenter
, slideTrigger_close: "mouseleave"// click, mouseleave
, slideDelay_open: 300 // applies only for mouseenter event - 0 = instant open
, slideDelay_close: 300 // applies only for mouseleave event (300ms is the minimum!)
, hideTogglerOnSlide: false // when pane is slid-open, should the toggler show?
, preventQuickSlideClose: !!($.browser.webkit || $.browser.safari) // Chrome triggers slideClosed as is opening
, preventPrematureSlideClose: false
// HOT-KEYS & MISC
, 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'
// PANE ANIMATION
// 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 }
, fxOpacityFix: true // tries to fix opacity in IE to restore anti-aliasing after animation
// CALLBACKS
, triggerEventsOnLoad: false // true = trigger onopen OR onclose callbacks when layout initializes
, triggerEventsWhileDragging: true // true = trigger onresize callback REPEATEDLY if resizeWhileDragging==true
, onshow_start: null // CALLBACK when pane STARTS to Show - BEFORE onopen/onhide_start
, onshow_end: null // CALLBACK when pane ENDS being Shown - AFTER onopen/onhide_end
, onhide_start: null // CALLBACK when pane STARTS to Close - BEFORE onclose_start
, onhide_end: null // CALLBACK when pane ENDS being Closed - AFTER onclose_end
, onopen_start: null // CALLBACK when pane STARTS to Open
, onopen_end: null // CALLBACK when pane ENDS being Opened
, onclose_start: null // CALLBACK when pane STARTS to Close
, onclose_end: null // CALLBACK when pane ENDS being Closed
, onresize_start: null // CALLBACK when pane STARTS being Resized ***FOR ANY REASON***
, onresize_end: null // CALLBACK when pane ENDS being Resized ***FOR ANY REASON***
, onsizecontent_start: null // CALLBACK when sizing of content-element STARTS
, onsizecontent_end: null // CALLBACK when sizing of content-element ENDS
, onswap_start: null // CALLBACK when pane STARTS to Swap
, onswap_end: null // CALLBACK when pane ENDS being Swapped
, ondrag_start: null // CALLBACK when pane STARTS being ***MANUALLY*** Resized
, ondrag_end: null // CALLBACK when pane ENDS being ***MANUALLY*** Resized
}
, north: {
paneSelector: ".ui-layout-north"
, size: "auto" // eg: "auto", "30%", 200
, resizerCursor: "n-resize" // custom = url(myCursor.cur)
, customHotkey: "" // EITHER a charCode OR a character
}
, south: {
paneSelector: ".ui-layout-south"
, size: "auto"
, resizerCursor: "s-resize"
, customHotkey: ""
}
, east: {
paneSelector: ".ui-layout-east"
, size: 200
, resizerCursor: "e-resize"
, customHotkey: ""
}
, west: {
paneSelector: ".ui-layout-west"
, size: 200
, resizerCursor: "w-resize"
, customHotkey: ""
}
, center: {
paneSelector: ".ui-layout-center"
, minWidth: 0
, minHeight: 0
}
// STATE MANAGMENT
, useStateCookie: false // Enable cookie-based state-management - can fine-tune with cookie.autoLoad/autoSave
, cookie: {
name: "" // If not specified, will use Layout.name, else just "Layout"
, autoSave: true // Save a state cookie when page exits?
, autoLoad: true // Load the state cookie when Layout inits?
// Cookie Options
, domain: ""
, path: ""
, expires: "" // 'days' to keep cookie - leave blank for 'session cookie'
, secure: false
// List of options to save in the cookie - must be pane-specific
, keys: "north.size,south.size,east.size,west.size,"+
"north.isClosed,south.isClosed,east.isClosed,west.isClosed,"+
"north.isHidden,south.isHidden,east.isHidden,west.isHidden"
}
};
// PREDEFINED EFFECTS / DEFAULTS
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" }
}
};
// DYNAMIC DATA - IS READ-ONLY EXTERNALLY!
var state = {
// generate unique ID to use for event.namespace so can unbind only events added by 'this layout'
id: "layout"+ new Date().getTime() // code uses alias: sID
, initialized: false
, container: {} // init all keys
, north: {}
, south: {}
, east: {}
, west: {}
, center: {}
, cookie: {} // State Managment data storage
};
// INTERNAL CONFIG DATA - DO NOT CHANGE THIS!
var _c = {
allPanes: "north,south,west,east,center"
, borderPanes: "north,south,west,east"
, altSide: {
north: "south"
, south: "north"
, east: "west"
, west: "east"
}
// CSS used in multiple places
, hidden: { visibility: "hidden" }
, visible: { visibility: "visible" }
// layout element settings
, zIndex: { // set z-index values here
pane_normal: 1 // normal z-index for panes
, resizer_normal: 2 // normal z-index for resizer-bars
, iframe_mask: 2 // overlay div used to mask pane(s) during resizing
, pane_sliding: 100 // applied to *BOTH* the pane and its resizer when a pane is 'slid open'
, pane_animate: 1000 // applied to the pane when being animated - not applied to the resizer
, resizer_drag: 10000 // applied to the CLONED resizer-bar when being 'dragged'
}
, resizers: {
cssReq: {
position: "absolute"
, padding: 0
, margin: 0
, fontSize: "1px"
, textAlign: "left" // to counter-act "center" alignment!
, overflow: "hidden" // prevent toggler-button from overflowing
// SEE c.zIndex.resizer_normal
}
, cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=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
}
, cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=true
background: "#AAA"
}
}
, content: {
cssReq: {
position: "relative" /* contain floated or positioned elements */
}
, cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=true
overflow: "auto"
, padding: "10px"
}
, cssDemoPane: { // DEMO CSS - REMOVE scrolling from 'pane' when it has a content-div
overflow: "hidden"
, padding: 0
}
}
, panes: { // defaults for ALL panes - overridden by 'per-pane settings' below
cssReq: {
position: "absolute"
, margin: 0
// SEE c.zIndex.pane_normal
}
, cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=true
padding: "10px"
, background: "#FFF"
, border: "1px solid #BBB"
, overflow: "auto"
}
}
, north: {
side: "Top"
, sizeType: "Height"
, dir: "horz"
, cssReq: {
top: 0
, bottom: "auto"
, left: 0
, right: 0
, width: "auto"
// height: DYNAMIC
}
, pins: [] // array of 'pin buttons' to be auto-updated on open/close (classNames)
}
, south: {
side: "Bottom"
, sizeType: "Height"
, dir: "horz"
, cssReq: {
top: "auto"
, bottom: 0
, left: 0
, right: 0
, width: "auto"
// height: DYNAMIC
}
, pins: []
}
, east: {
side: "Right"
, sizeType: "Width"
, dir: "vert"
, cssReq: {
left: "auto"
, right: 0
, top: "auto" // DYNAMIC
, bottom: "auto" // DYNAMIC
, height: "auto"
// width: DYNAMIC
}
, pins: []
}
, west: {
side: "Left"
, sizeType: "Width"
, dir: "vert"
, cssReq: {
left: 0
, right: "auto"
, top: "auto" // DYNAMIC
, bottom: "auto" // DYNAMIC
, height: "auto"
// width: DYNAMIC
}
, pins: []
}
, center: {
dir: "center"
, cssReq: {
left: "auto" // DYNAMIC
, right: "auto" // DYNAMIC
, top: "auto" // DYNAMIC
, bottom: "auto" // DYNAMIC
, height: "auto"
, width: "auto"
}
}
};
/*
* ###########################
* INTERNAL HELPER FUNCTIONS
* ###########################
*/
/**
* Manages all internal timers
*/
var timer = {
data: {}
, set: function (s, fn, ms) { timer.clear(s); timer.data[s] = setTimeout(fn, ms); }
, clear: function (s) { var t=timer.data; if (t[s]) {clearTimeout(t[s]); delete t[s];} }
};
/**
* Returns true if passed param is EITHER a simple string OR a 'string object' - otherwise returns false
*/
var isStr = function (o) {
try { return typeof o == "string"
|| (typeof o == "object" && o.constructor.toString().match(/string/i) !== null); }
catch (e) { return false; }
};
/**
* Returns a simple string if passed EITHER a simple string OR a 'string object',
* else returns the original object
*/
var str = function (o) { // trim converts 'String object' to a simple string
return isStr(o) ? $.trim(o) : o == undefined || o == null ? "" : o;
};
/**
* min / max
*
* Aliases for Math methods to simplify coding
*/
var min = function (x,y) { return Math.min(x,y); };
var max = function (x,y) { return Math.max(x,y); };
/**
* 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 ???
*
* @see initOptions()
* @param {Object} d Data/options passed by user - may be a single level or nested levels
* @return {Object} Creates a data struture that perfectly matches 'options', ready to be imported
*/
var _transformData = function (d) {
var a, json = { cookie:{}, defaults:{fxSettings:{}}, north:{fxSettings:{}}, south:{fxSettings:{}}, east:{fxSettings:{}}, west:{fxSettings:{}}, center:{fxSettings:{}} };
d = d || {};
if (d.effects || d.cookie || d.defaults || d.north || d.south || d.west || d.east || d.center)
json = $.extend( true, 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("__");
if (!a[1] || json[a[0]]) // check for invalid keys
json[ a[1] ? a[0] : "defaults" ][ a[1] ? a[1] : a[0] ] = val;
});
return json;
};
/**
* Set an INTERNAL callback to avoid simultaneous animation
* Runs only if needed and only if all callbacks are not 'already set'
* Called by open() and close() when isLayoutBusy=true
*
* @param {string} action Either 'open' or 'close'
* @param {string} pane A valid border-pane name, eg 'west'
* @param {boolean=} param Extra param for callback (optional)
*/
var _queue = function (action, pane, param) {
var tried = [];
// if isLayoutBusy, then some pane must be 'moving'
$.each(_c.borderPanes.split(","), function (i, p) {
if (_c[p].isMoving) {
bindCallback(p); // TRY to bind a callback
return false; // BREAK
}
});
// if pane does NOT have a callback, then add one, else follow the callback chain...
function bindCallback (p) {
var c = _c[p];
if (!c.doCallback) {
c.doCallback = true;
c.callback = action +","+ pane +","+ (param ? 1 : 0);
}
else { // try to 'chain' this callback
tried.push(p);
var cbPane = c.callback.split(",")[1]; // 2nd param of callback is 'pane'
// ensure callback target NOT 'itself' and NOT 'target pane' and NOT already tried (avoid loop)
if (cbPane != pane && !$.inArray(cbPane, tried) >= 0)
bindCallback(cbPane); // RECURSE
}
}
};
/**
* RUN the INTERNAL callback for this pane - if one exists
*
* @param {string} pane A valid border-pane name, eg 'west'
*/
var _dequeue = function (pane) {
var c = _c[pane];
// RESET flow-control flags
_c.isLayoutBusy = false;
delete c.isMoving;
if (!c.doCallback || !c.callback) return;
c.doCallback = false; // RESET logic flag
// EXECUTE the callback
var
cb = c.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 (!c.doCallback) c.callback = null; // RESET - unless callback above enabled it again!
};
/**
* 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|function())} v_fn Accepts a function name, OR a comma-delimited array: [0]=function name, [1]=argument
*/
var _execCallback = function (pane, v_fn) {
if (!v_fn) return;
var fn;
try {
if (typeof v_fn == "function")
fn = v_fn;
else if (!isStr(v_fn))
return;
else if (v_fn.match(/,/)) {
// 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") {
if (pane && $Ps[pane])
// pass data: pane-name, pane-element, pane-state (copy), pane-options, and layout-name
return fn( pane, $Ps[pane], $.extend({},state[pane]), options[pane], options.name );
else // must be a layout/container callback - pass suitable info
return fn( Instance, $.extend({},state), options, options.name );
}
}
catch (ex) {}
};
/**
* Returns hash container 'display' and 'visibility'
*
* @see $.swap() - swaps CSS, runs callback, resets CSS
* @param {!Object} $E
* @param {boolean=} force
*/
var _showInvisibly = function ($E, force) {
if (!$E) return {};
if (!$E.jquery) $E = $($E);
var CSS = {
display: $E.css('display')
, visibility: $E.css('visibility')
};
if (force || CSS.display == "none") { // only if not *already hidden*
$E.css({ display: "block", visibility: "hidden" }); // show element 'invisibly' so can be measured
return CSS;
}
else return {};
};
/**
* cure iframe display issues in IE & other browsers
*/
var _fixIframe = function (pane) {
if (state.browser.mozilla) return; // skip FireFox - it auto-refreshes iframes onShow
var $P = $Ps[pane];
// if the 'pane' is an iframe, do it
if (state[pane].tagName == "IFRAME")
$P.css(_c.hidden).css(_c.visible);
else // ditto for any iframes INSIDE the pane
$P.find('IFRAME').css(_c.hidden).css(_c.visible);
};
/**
* Returns the 'current CSS numeric value' for a CSS property - 0 if property does not exist
*
* @see Called by many methods
* @param {Array.<Object>} $E Must pass a jQuery object - first element is processed
* @param {string} prop The name of the CSS property, eg: top, width, etc.
* @return {(string|number)} Usually used to get an integer value for position (top, left) or size (height, width)
*/
var _cssNum = function ($E, prop) {
if (!$E.jquery) $E = $($E);
var CSS = _showInvisibly($E);
var val = parseInt($.curCSS($E[0], prop, true), 10) || 0;
$E.css( CSS ); // RESET
return val;
};
/**
* @param {!Object} E Can accept a 'pane' (east, west, etc) OR a DOM object OR a jQuery object
* @param {string} side Which border (top, left, etc.) is resized
* @return {number} Returns the borderWidth
*/
var _borderWidth = function (E, side) {
if (E.jquery) E = E[0];
var b = "border"+ side.substr(0,1).toUpperCase() + side.substr(1); // left => Left
return $.curCSS(E, b+"Style", true) == "none" ? 0 : (parseInt($.curCSS(E, b+"Width", true), 10) || 0);
};
/**
* cssW / cssH / cssSize / cssMinDims
*
* Contains logic to check boxModel & browser, and return the correct width/height for the current browser/doctype
*
* @see initPanes(), sizeMidPanes(), initHandles(), sizeHandles()
* @param {(string|!Object)} el Can accept a 'pane' (east, west, etc) OR a DOM object OR a jQuery object
* @param {number=} outerWidth (optional) Can pass a width, allowing calculations BEFORE element is resized
* @return {number} Returns the innerWidth of el by subtracting padding and borders
*/
var cssW = function (el, outerWidth) {
var
str = isStr(el)
, $E = str ? $Ps[el] : $(el)
;
if (isNaN(outerWidth)) // not specified
outerWidth = str ? getPaneSize(el) : $E.outerWidth();
// a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
if (outerWidth <= 0) return 0;
if (!state.browser.boxModel) return outerWidth;
// strip border and padding from outerWidth to get CSS Width
var W = outerWidth
- _borderWidth($E, "Left")
- _borderWidth($E, "Right")
- _cssNum($E, "paddingLeft")
- _cssNum($E, "paddingRight")
;
return max(0,W);
};
/**
* @param {(string|!Object)} el Can accept a 'pane' (east, west, etc) OR a DOM object OR a jQuery object
* @param {number=} outerHeight (optional) Can pass a width, allowing calculations BEFORE element is resized
* @return {number} Returns the innerHeight el by subtracting padding and borders
*/
var cssH = function (el, outerHeight) {
var
str = isStr(el)
, $E = str ? $Ps[el] : $(el)
;
if (isNaN(outerHeight)) // not specified
outerHeight = str ? getPaneSize(el) : $E.outerHeight();
// a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
if (outerHeight <= 0) return 0;
if (!state.browser.boxModel) return outerHeight;
// strip border and padding from outerHeight to get CSS Height
var H = outerHeight
- _borderWidth($E, "Top")
- _borderWidth($E, "Bottom")
- _cssNum($E, "paddingTop")
- _cssNum($E, "paddingBottom")
;
return max(0,H);
};
/**
* @param {string} pane Can accept ONLY a 'pane' (east, west, etc)
* @param {number=} outerSize (optional) Can pass a width, allowing calculations BEFORE element is resized
* @return {number} Returns the innerHeight/Width of el by subtracting padding and borders
*/
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);
};
/**
* @param {string} pane Can accept ONLY a 'pane' (east, west, etc)
* @return {Object} Returns hash of minWidth & minHeight
*/
var cssMinDims = function (pane) {
// minWidth/Height means CSS width/height = 1px
var
dir = _c[pane].dir
, d = {
minWidth: 1001 - cssW(pane, 1000)
, minHeight: 1001 - cssH(pane, 1000)
}
;
if (dir == "horz") d.minSize = d.minHeight;
if (dir == "vert") d.minSize = d.minWidth;
return d;
};
// TODO: see if these methods can be made more useful...
// TODO: *maybe* return cssW/H from these so caller can use this info
/**
* @param {(string|!Object)} el
* @param {number=} outerWidth
* @param {boolean=} autoHide
*/
var setOuterWidth = function (el, outerWidth, autoHide) {
var $E = el, w;
if (isStr(el)) $E = $Ps[el]; // west
else if (!el.jquery) $E = $(el);
w = cssW($E, outerWidth);
$E.css({ width: w });
if (w > 0) {
if (autoHide && $E.data('autoHidden') && $E.innerHeight() > 0) {
$E.show().data('autoHidden', false);
if (!state.browser.mozilla) // FireFox refreshes iframes - IE doesn't
// make hidden, then visible to 'refresh' display after animation
$E.css(_c.hidden).css(_c.visible);
}
}
else if (autoHide && !$E.data('autoHidden'))
$E.hide().data('autoHidden', true);
};
/**
* @param {(string|!Object)} el
* @param {number=} outerHeight
* @param {boolean=} autoHide
*/
var setOuterHeight = function (el, outerHeight, autoHide) {
var $E = el, h;
if (isStr(el)) $E = $Ps[el]; // west
else if (!el.jquery) $E = $(el);
h = cssH($E, outerHeight);
$E.css({ height: h, visibility: "visible" }); // may have been 'hidden' by sizeContent
if (h > 0 && $E.innerWidth() > 0) {
if (autoHide && $E.data('autoHidden')) {
$E.show().data('autoHidden', false);
if (!state.browser.mozilla) // FireFox refreshes iframes - IE doesn't
$E.css(_c.hidden).css(_c.visible);
}
}
else if (autoHide && !$E.data('autoHidden'))
$E.hide().data('autoHidden', true);
};
/**
* @param {(string|!Object)} el
* @param {number=} outerSize
* @param {boolean=} autoHide
*/
var setOuterSize = function (el, outerSize, autoHide) {
if (_c[pane].dir=="horz") // pane = north or south
setOuterHeight(el, outerSize, autoHide);
else // pane = east or west
setOuterWidth(el, outerSize, autoHide);
};
/**
* Converts any 'size' params to a pixel/integer size, if not already
* If 'auto' or a decimal/percentage is passed as 'size', a pixel-size is calculated
*
/**
* @param {string} pane
* @param {(string|number)=} size
* @param {string=} dir
* @return {number}
*/
var _parseSize = function (pane, size, dir) {
if (!dir) dir = _c[pane].dir;
if (isStr(size) && size.match(/%/))
size = parseInt(size, 10) / 100; // convert % to decimal
if (size === 0)
return 0;
else if (size >= 1)
return parseInt(size, 10);
else if (size > 0) { // percentage, eg: .25
var o = options, avail;
if (dir=="horz") // north or south or center.minHeight
avail = sC.innerHeight - ($Ps.north ? o.north.spacing_open : 0) - ($Ps.south ? o.south.spacing_open : 0);
else if (dir=="vert") // east or west or center.minWidth
avail = sC.innerWidth - ($Ps.west ? o.west.spacing_open : 0) - ($Ps.east ? o.east.spacing_open : 0);
return Math.floor(avail * size);
}
else if (pane=="center")
return 0;
else { // size < 0 || size=='auto' || size==Missing || size==Invalid
// auto-size the pane
var
$P = $Ps[pane]
, dim = (dir == "horz" ? "height" : "width")
, vis = _showInvisibly($P) // show pane invisibly if hidden
, s = $P.css(dim); // SAVE current size
;
$P.css(dim, "auto");
size = (dim == "height") ? $P.outerHeight() : $P.outerWidth(); // MEASURE
$P.css(dim, s).css(vis); // RESET size & visibility
return size;
}
};
/**
* Calculates current 'size' (outer-width or outer-height) of a border-pane - optionally with 'pane-spacing' added
*
* @param {(string|!Object)} pane
* @param {boolean=} inclSpace
* @return {number} 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;
};
/**
* Calculate min/max pane dimensions and limits for resizing
*
* @param {string} pane
* @param {boolean=} slide
*/
var setSizeLimits = function (pane, slide) {
var
o = options[pane]
, s = state[pane]
, c = _c[pane]
, dir = c.dir
, side = c.side.toLowerCase()
, type = c.sizeType.toLowerCase()
, isSliding = (slide != undefined ? slide : s.isSliding) // only open() passes 'slide' param
, $P = $Ps[pane]
, paneSpacing = o.spacing_open
// measure the pane on the *opposite side* from this pane
, altPane = _c.altSide[pane]
, altS = state[altPane]
, $altP = $Ps[altPane]
, altPaneSize = (!$altP || altS.isVisible===false || altS.isSliding ? 0 : (dir=="horz" ? $altP.outerHeight() : $altP.outerWidth()))
, altPaneSpacing = ((!$altP || altS.isHidden ? 0 : options[altPane][ altS.isClosed !== false ? "spacing_closed" : "spacing_open" ]) || 0)
// limitSize prevents this pane from 'overlapping' opposite pane
, containerSize = (dir=="horz" ? sC.innerHeight : sC.innerWidth)
, minCenterDims = cssMinDims("center")
, minCenterSize = dir=="horz" ? max(options.center.minHeight, minCenterDims.minHeight) : max(options.center.minWidth, minCenterDims.minWidth)
// if pane is 'sliding', then ignore center and alt-pane sizes - because 'overlays' them
, limitSize = (containerSize - paneSpacing - (isSliding ? 0 : (_parseSize("center", minCenterSize, dir) + altPaneSize + altPaneSpacing)))
, minSize = s.minSize = max( _parseSize(pane, o.minSize), cssMinDims(pane).minSize )
, maxSize = s.maxSize = min( (o.maxSize ? _parseSize(pane, o.maxSize) : 100000), limitSize )
, r = s.resizerPosition = {} // used to set resizing limits
, top = sC.insetTop
, left = sC.insetLeft
, W = sC.innerWidth
, H = sC.innerHeight
, rW = o.spacing_open // subtract resizer-width to get top/left position for south/east
;
switch (pane) {
case "north": r.min = top + minSize;
r.max = top + maxSize;
break;
case "west": r.min = left + minSize;
r.max = left + maxSize;
break;
case "south": r.min = top + H - maxSize - rW;
r.max = top + H - minSize - rW;
break;
case "east": r.min = left + W - maxSize - rW;
r.max = left + W - minSize - rW;
break;
};
};
/**
* Returns data for setting the size/position of center pane. Also used to set Height for east/west panes
*
* @return JSON Returns a hash of all dimensions: top, bottom, left, right, (outer) width and (outer) height
*/
var calcNewCenterPaneDims = function () {
var d = {
top: getPaneSize("north", true) // true = include 'spacing' value for pane
, bottom: getPaneSize("south", true)
, left: getPaneSize("west", true)
, right: getPaneSize("east", true)
, width: 0
, height: 0
};
// NOTE: sC = state.container
// calc center-pane's outer dimensions
d.width = sC.innerWidth - d.left - d.right; // outerWidth
d.height = sC.innerHeight - d.bottom - d.top; // outerHeight
// add the 'container border/padding' to get final positions relative to the container
d.top += sC.insetTop;
d.bottom += sC.insetBottom;
d.left += sC.insetLeft;
d.right += sC.insetRight;
return d;
};
/**
* Returns data for setting size of an element (container or a pane).
*
* @see _create(), onWindowResize() for container, plus others for pane
* @return JSON Returns a hash of all dimensions: top, bottom, left, right, outerWidth, innerHeight, etc
*/
var getElemDims = function ($E) {
var
d = {} // dimensions hash
, x = d.css = {} // CSS hash
, i = {} // TEMP insets
, b, p // TEMP border, padding
, off = $E.offset()
;
d.offsetLeft = off.left;
d.offsetTop = off.top;
$.each("Left,Right,Top,Bottom".split(","), function (idx, e) {
b = x["border" + e] = _borderWidth($E, e);
p = x["padding"+ e] = _cssNum($E, "padding"+e);
i[e] = b + p; // total offset of content from outer side
d["inset"+ e] = p;
/* WRONG ???
// if BOX MODEL, then 'position' = PADDING (ignore borderWidth)
if ($E == $Container)
d["inset"+ e] = (state.browser.boxModel ? p : 0);
*/
});
d.offsetWidth = $E.innerWidth(); // true=include Padding
d.offsetHeight = $E.innerHeight();
d.outerWidth = $E.outerWidth();
d.outerHeight = $E.outerHeight();
d.innerWidth = d.outerWidth - i.Left - i.Right;
d.innerHeight = d.outerHeight - i.Top - i.Bottom;
// TESTING
x.width = $E.width();
x.height = $E.height();
return d;
};
var getElemCSS = function ($E, list) {
var
CSS = {}
, style = $E[0].style
, props = list.split(",")
, sides = "Top,Bottom,Left,Right".split(",")
, attrs = "Color,Style,Width".split(",")
, p, s, a, i, j, k
;
for (i=0; i < props.length; i++) {
p = props[i];
if (p.match(/(border|padding|margin)$/))
for (j=0; j < 4; j++) {
s = sides[j];
if (p == "border")
for (k=0; k < 3; k++) {
a = attrs[k];
CSS[p+s+a] = style[p+s+a];
}
else
CSS[p+s] = style[p+s];
}
else
CSS[p] = style[p];
};
return CSS
};
/**
* @param {!Object} el
* @param {boolean=} allStates
*/
var getHoverClasses = function (el, allStates) {
var
$El = $(el)
, type = $El.data("layoutRole")
, pane = $El.data("layoutEdge")
, o = options[pane]
, root = o[type +"Class"]
, _pane = "-"+ pane // eg: "-west"
, _open = "-open"
, _closed = "-closed"
, _slide = "-sliding"
, _hover = "-hover " // NOTE the trailing space
, _state = $El.hasClass(root+_closed) ? _closed : _open
, _alt = _state == _closed ? _open : _closed
, classes = (root+_hover) + (root+_pane+_hover) + (root+_state+_hover) + (root+_pane+_state+_hover)
;
if (allStates) // when 'removing' classes, also remove alternate-state classes
classes += (root+_alt+_hover) + (root+_pane+_alt+_hover);
if (type=="resizer" && $El.hasClass(root+_slide))
classes += (root+_slide+_hover) + (root+_pane+_slide+_hover);
return $.trim(classes);
};
var addHover = function (evt, el) {
var $E = $(el || this);
if (evt && $E.data("layoutRole") == "toggler")
evt.stopPropagation(); // prevent triggering 'slide' on Resizer-bar
$E.addClass( getHoverClasses($E) );
};
var removeHover = function (evt, el) {
var $E = $(el || this);
$E.removeClass( getHoverClasses($E, true) );
};
var onResizerEnter = function (evt) {
$('body').disableSelection();
addHover(evt, this);
};
var onResizerLeave = function (evt, el) {
var
e = el || this // el is only passed when called by the timer
, pane = $(e).data("layoutEdge")
, name = pane +"ResizerLeave"
;
timer.clear(pane+"_openSlider"); // cancel slideOpen timer, if set
timer.clear(name); // cancel enableSelection timer - may re/set below
if (!el) { // 1st call - mouseleave event
removeHover(evt, this); // do this on initial call
// this method calls itself on a timer because it needs to allow
// enough time for dragging to kick-in and set the isResizing flag
// dragging has a 100ms delay set, so this delay must be higher
timer.set(name, function(){ onResizerLeave(evt, e); }, 200);
}
// if user is resizing, then dragStop will enableSelection() when done
else if (!state[pane].isResizing) // 2nd call - by timer
$('body').enableSelection();
};
/*
* ###########################
* INITIALIZATION METHODS
* ###########################
*/
/**
* Initialize the layout - called automatically whenever an instance of layout is created
*
* @see none - triggered onInit
* @return An object pointer to the instance created
*/
var _create = function () {
// initialize config/options
initOptions();
var o = options;
// onload will CANCEL resizing if returns false
if (false === _execCallback(null, o.onload_start)) return false;
// a center pane is required, so make sure it exists
if (!getPane('center').length) {
alert( lang.errCenterPaneMissing );
return null;
}
// update options with saved state, if option enabled
if (o.useStateCookie && o.cookie.autoLoad)
loadCookie(); // Update options from state-cookie
// set environment - can update code here if $.browser is phased out
state.browser = {
mozilla: $.browser.mozilla
, webkit: $.browser.webkit || $.browser.safari
, msie: $.browser.msie
, isIE6: $.browser.msie && $.browser.version == 6
, boxModel: $.support.boxModel
//, version: $.browser.version - not used
};
// initialize all layout elements
initContainer(); // set CSS as needed and init state.container dimensions
initPanes(); // size & position panes - calls initHandles() - which calls initResizable()
sizeContent(); // AFTER panes & handles have been initialized, size 'content' divs
if (o.scrollToBookmarkOnLoad) {
var l = self.location;
if (l.hash) l.replace( l.hash ); // scrollTo Bookmark
}
// bind hotkey function - keyDown - if required
initHotkeys();
// search for and bind custom-buttons
if (o.autoBindCustomButtons) initButtons();
// bind resizeAll() for 'this layout instance' to window.resize event
if (o.resizeWithWindow && !$Container.data("layoutRole")) // skip if 'nested' inside a pane
$(window).bind("resize."+ sID, windowResize);
// bind window.onunload
$(window).bind("unload."+ sID, unload);
state.initialized = true;
_execCallback(null, o.onload_end || o.onload);
};
var windowResize = function () {
var delay = Number(options.resizeWithWindowDelay) || 100; // there MUST be some delay!
if (delay > 0) {
// resizing uses a delay-loop because the resize event fires repeatly - except in FF, but delay anyway
timer.clear("winResize"); // if already running
timer.set("winResize", function(){ timer.clear("winResize"); timer.clear("winResizeRepeater"); resizeAll(); }, delay);
// ALSO set fixed-delay timer, if not already running
if (!timer.data["winResizeRepeater"]) setWindowResizeRepeater();
}
};
var setWindowResizeRepeater = function () {
var delay = Number(options.resizeWithWindowMaxDelay);
if (delay > 0)
timer.set("winResizeRepeater", function(){ setWindowResizeRepeater(); resizeAll(); }, delay);
};
var unload = function () {
var o = options;
state.cookie = getState(); // save state in case onunload has custom state-management
_execCallback(null, o.onunload_start);
if (o.useStateCookie && o.cookie.autoSave) saveCookie();
_execCallback(null, o.onunload_end || o.onunload);
};
/**
* Validate and initialize container CSS and events
*
* @see _create()
*/
var initContainer = function () {
var
$C = $Container // alias
, tag = sC.tagName = $C.attr("tagName")
, fullPage= (tag == "BODY")
, props = "position,margin,padding,border"
, CSS = {}
;
sC.selector = $C.selector.split(".slice")[0];
sC.ref = tag +"/"+ sC.selector; // used in messages
$C .data("layout", Instance)
.data("layoutContainer", sID) // unique identifier for internal use
.addClass(options.containerClass)
;
// SAVE original container CSS for use in destroy()
if (!$C.data("layoutCSS")) {
// handle props like overflow different for BODY & HTML - has 'system default' values
if (fullPage) {
CSS = $.extend( getElemCSS($C, props), {
height: $C.css("height")
, overflow: $C.css("overflow")
, overflowX: $C.css("overflowX")
, overflowY: $C.css("overflowY")
});
// ALSO SAVE <HTML> CSS
var $H = $("html");
$H.data("layoutCSS", {
height: "auto" // FF would return a fixed px-size!
, overflow: $H.css("overflow")
, overflowX: $H.css("overflowX")
, overflowY: $H.css("overflowY")
});
}
else // handle props normally for non-body elements
CSS = getElemCSS($C, props+",top,bottom,left,right,width,height,overflow,overflowX,overflowY");
$C.data("layoutCSS", CSS);
}
try { // format html/body if this is a full page layout
if (fullPage) {
$("html").css({
height: "100%"
, overflow: "hidden"
, overflowX: "hidden"
, overflowY: "hidden"
});
$("body").css({
position: "relative"
, height: "100%"
, overflow: "hidden"
, overflowX: "hidden"
, overflowY: "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 for overflow and position
CSS = { overflow: "hidden" } // make sure container will not 'scroll'
var
p = $C.css("position")
, h = $C.css("height")
;
// if this is a NESTED layout, then container/outer-pane ALREADY has position and height
if (!$C.data("layoutRole")) {
if (!p || !p.match(/fixed|absolute|relative/))
CSS.position = "relative"; // container MUST have a 'position'
/*
if (!h || h=="auto")
CSS.height = "100%"; // container MUST have a 'height'
*/
}
$C.css( CSS );
if ($C.is(":visible") && $C.innerHeight() < 2)
alert( lang.errContainerHeight.replace(/CONTAINER/, sC.ref) );
}
} catch (ex) {}
// set current layout-container dimensions
$.extend(state.container, getElemDims( $C ));
};
/**
* Bind layout hotkeys - if options enabled
*
* @see _create() and addPane()
* @param {string=} panes The edge(s) to process, blank = all
*/
var initHotkeys = function (panes) {
if (!panes || panes == "all") panes = _c.borderPanes;
// bind keyDown to capture hotkeys, if option enabled for ANY pane
$.each(panes.split(","), function (i, pane) {
var o = options[pane];
if (o.enableCursorHotkey || o.customHotkey) {
$(document).bind("keydown."+ sID, keyDown); // only need to bind this ONCE
return false; // BREAK - binding was done
}
});
};
/**
* Build final OPTIONS data
*
* @see _create()
*/
var initOptions = function () {
// simplify logic by making sure passed 'opts' var has basic keys
opts = _transformData( opts );
// TODO: create a compatibility add-on for new UI widget that will transform old option syntax
var newOpts = {
applyDefaultStyles: "applyDemoStyles"
};
renameOpts(opts.defaults);
$.each(_c.allPanes.split(","), function (i, pane) {
renameOpts(opts[pane]);
});
// update default effects, if case user passed key
if (opts.effects) {
$.extend( effects, opts.effects );
delete opts.effects;
}
$.extend( options.cookie, opts.cookie );
// see if any 'global options' were specified
var globals = "name,containerClass,zIndex,scrollToBookmarkOnLoad,resizeWithWindow,resizeWithWindowDelay,resizeWithWindowMaxDelay,"+
"onresizeall,onresizeall_start,onresizeall_end,onload,onload_start,onload_end,onunload,onunload_start,onunload_end,autoBindCustomButtons,useStateCookie";
$.each(globals.split(","), function (i, 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 (i, key) { delete opts.defaults[key]; } // is OK if key does not exist
);
// now update options.defaults
$.extend( true, options.defaults, opts.defaults );
// merge config for 'center-pane' - border-panes handled in the loop below
_c.center = $.extend( true, {}, _c.panes, _c.center );
// update config.zIndex values if zIndex option specified
var z = options.zIndex;
if (z === 0 || z > 0) {
_c.zIndex.pane_normal = z;
_c.zIndex.resizer_normal = z+1;
_c.zIndex.iframe_mask = z+1;
}
// merge options for 'center-pane' - border-panes handled in the loop below
$.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
var optionsCenter = ("paneClass,contentSelector,applyDemoStyles,triggerEventsOnLoad,showOverflowOnHover,"
+ "onresize,onresize_start,onresize_end,resizeNestedLayout,resizeContentWhileDragging,"
+ "onsizecontent,onsizecontent_start,onsizecontent_end").split(",");
$.each(optionsCenter,
function (i, key) { options.center[key] = o_Center[key]; }
);
var o, 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.panes, _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 = "ui-layout-pane";
if (!o.resizerClass) o.resizerClass = "ui-layout-resizer";
if (!o.togglerClass) o.togglerClass = "ui-layout-toggler";
// 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
;
});
});
function renameOpts (O) {
for (var key in newOpts) {
if (O[key] != undefined) {
O[newOpts[key]] = O[key];
delete O[key];
}
}
}
};
/**
* Initialize module objects, styling, size and position for all panes
*
* @see _create()
* @param {string} pane The pane to process
*/
var getPane = function (pane) {
var sel = options[pane].paneSelector
if (sel.substr(0,1)==="#") // ID selector
// NOTE: elements selected 'by ID' DO NOT have to be 'children'
return $Container.find(sel).eq(0);
else { // class or other selector
var $P = $Container.children(sel).eq(0);
// look for the pane nested inside a 'form' element
return $P.length ? $P : $Container.children("form:first").children(sel).eq(0);
}
};
var initPanes = function () {
// NOTE: do north & south FIRST so we can measure their height - do center LAST
$.each(_c.allPanes.split(","), function (idx, pane) {
addPane( pane );
});
// init the pane-handles NOW in case we have to hide or close the pane below
initHandles();
// now that all panes have been initialized and initially-sized,
// make sure there is really enough space available for each pane
$.each(_c.borderPanes.split(","), function (i, pane) {
if ($Ps[pane] && state[pane].isVisible) { // pane is OPEN
setSizeLimits(pane);
makePaneFit(pane); // pane may be Closed, Hidden or Resized by makePaneFit()
}
});
// size center-pane AGAIN in case we 'closed' a border-pane in loop above
sizeMidPanes("center");
// trigger onResize callbacks for all panes with triggerEventsOnLoad = true
$.each(_c.allPanes.split(","), function (i, pane) {
var o = options[pane];
if ($Ps[pane] && state[pane].isVisible) { // pane is OPEN
if (o.triggerEventsOnLoad)
_execCallback(pane, o.onresize_end || o.onresize);
resizeNestedLayout(pane);
}
});
if ($Container.innerHeight() < 2)
alert( lang.errContainerHeight.replace(/CONTAINER/, sC.ref) );
};
/**
* Remove a pane from the layout - subroutine of destroy()
*
* @see initPanes()
* @param {string} pane The pane to process
*/
var addPane = function (pane) {
var
o = options[pane]
, s = state[pane]
, c = _c[pane]
, fx = s.fx
, dir = c.dir
, spacing = o.spacing_open || 0
, isCenter = (pane == "center")
, CSS = {}
, $P = $Ps[pane]
, size, minSize, maxSize
;
// if pane-pointer already exists, remove the old one first
if ($P)
removePane( pane );
else
$Cs[pane] = false; // init
$P = $Ps[pane] = getPane(pane);
if (!$P.length) {
$Ps[pane] = false; // logic
return;
}
// SAVE original Pane CSS
if (!$P.data("layoutCSS")) {
var props = "position,top,left,bottom,right,width,height,overflow,zIndex,display,backgroundColor,padding,margin,border";
$P.data("layoutCSS", getElemCSS($P, props));
}
// add basic classes & attributes
$P
.data("parentLayout", Instance)
.data("layoutRole", "pane")
.data("layoutEdge", pane)
.css(c.cssReq).css("zIndex", _c.zIndex.pane_normal)
.css(o.applyDemoStyles ? c.cssDemo : {}) // demo styles
.addClass( o.paneClass +" "+ o.paneClass+"-"+pane ) // default = "ui-layout-pane ui-layout-pane-west" - may be a dupe of 'paneSelector'
.bind("mouseenter."+ sID, addHover )
.bind("mouseleave."+ sID, removeHover )
;
// see if this pane has a 'scrolling-content element'
initContent(pane, false); // false = do NOT sizeContent() - called later
if (!isCenter) {
// call _parseSize AFTER applying pane classes & styles - but before making visible (if hidden)
// if o.size is auto or not valid, then MEASURE the pane and use that as it's 'size'
size = s.size = _parseSize(pane, o.size);
minSize = _parseSize(pane,o.minSize) || 1;
maxSize = _parseSize(pane,o.maxSize) || 100000;
if (size > 0) size = max(min(size, maxSize), minSize);
// state for border-panes
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!
}
// state for all panes
s.tagName = $P.attr("tagName");
s.edge = pane // useful if pane is (or about to be) 'swapped' - easy find out where it is (or is going)
s.noRoom = false; // true = pane 'automatically' hidden due to insufficient room - will unhide automatically
s.isVisible = true; // false = pane is invisible - closed OR hidden - simplify logic
// set css-position to account for container borders & padding
switch (pane) {
case "north": CSS.top = sC.insetTop;
CSS.left = sC.insetLeft;
CSS.right = sC.insetRight;
break;
case "south": CSS.bottom = sC.insetBottom;
CSS.left = sC.insetLeft;
CSS.right = sC.insetRight;
break;
case "west": CSS.left = sC.insetLeft; // top, bottom & height set by sizeMidPanes()
break;
case "east": CSS.right = sC.insetRight; // ditto
break;
case "center": // top, left, width & height set by sizeMidPanes()
}
if (dir == "horz") // north or south pane
CSS.height = max(1, cssH(pane, size));
else if (dir == "vert") // east or west pane
CSS.width = max(1, cssW(pane, size));
//else if (isCenter) {}
$P.css(CSS); // apply size -- top, bottom & height will be set by sizeMidPanes
if (dir != "horz") sizeMidPanes(pane, true); // true = skipCallback
// NOW make the pane visible - in case was initially hidden
if (!s.noRoom)
$P.css({ visibility: "visible", display: "block" });
// close or hide the pane if specified in settings
if (o.initClosed && o.closable)
close(pane, true, true); // true, true = force, noAnimation
else if (o.initHidden || o.initClosed)
hide(pane); // will be completely invisible - no resizer or spacing
// ELSE setAsOpen() - called later by initHandles()
// check option for auto-handling of pop-ups & drop-downs
if (o.showOverflowOnHover)
$P.hover( allowOverflow, resetOverflow );
// if adding a pane AFTER initialization, then...
if (state.initialized) {
initHandles( pane );
initHotkeys( pane );
resizeAll(); // will sizeContent if pane is visible
if (s.isVisible) { // pane is OPEN
if (o.triggerEventsOnLoad)
_execCallback(pane, o.onresize_end || o.onresize);
resizeNestedLayout(pane);
}
}
};
/**
* Initialize module objects, styling, size and position for all resize bars and toggler buttons
*
* @see _create()
* @param {string=} panes The edge(s) to process, blank = all
*/
var initHandles = function (panes) {
if (!panes || panes == "all") panes = _c.borderPanes;
// create toggler DIVs for each pane, and set object pointers for them, eg: $R.north = north toggler DIV
$.each(panes.split(","), function (i, pane) {
var $P = $Ps[pane];
$Rs[pane] = false; // INIT
$Ts[pane] = false;
if (!$P) return; // pane does not exist - skip
var
o = options[pane]
, s = state[pane]
, c = _c[pane]
, rClass = o.resizerClass
, tClass = o.togglerClass
, side = c.side.toLowerCase()
, spacing = (s.isVisible ? o.spacing_open : o.spacing_closed)
, _pane = "-"+ pane // used for classNames
, _state = (s.isVisible ? "-open" : "-closed") // used for classNames
// INIT RESIZER BAR
, $R = $Rs[pane] = $("<div></div>")
// INIT TOGGLER BUTTON
, $T = (o.closable ? $Ts[pane] = $("<div></div>") : false)
;
//if (s.isVisible && o.resizable) ... handled by initResizable
if (!s.isVisible && 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" : ""))
.data("parentLayout", Instance)
.data("layoutRole", "resizer")
.data("layoutEdge", pane)
.css(_c.resizers.cssReq).css("zIndex", _c.zIndex.resizer_normal)
.css(o.applyDemoStyles ? _c.resizers.cssDemo : {}) // add demo styles
.addClass(rClass +" "+ rClass+_pane)
.appendTo($Container) // append DIV to container
;
if ($T) {
$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" : ""))
.data("parentLayout", Instance)
.data("layoutRole", "toggler")
.data("layoutEdge", pane)
.css(_c.togglers.cssReq) // add base/required styles
.css(o.applyDemoStyles ? _c.togglers.cssDemo : {}) // add demo styles
.addClass(tClass +" "+ tClass+_pane)
.appendTo($R) // append SPAN to resizer DIV
;
// ADD INNER-SPANS TO TOGGLER
if (o.togglerContent_open) // ui-layout-open
$("<span>"+ o.togglerContent_open +"</span>")
.data("layoutRole", "togglerContent")
.data("layoutEdge", pane)
.addClass("content content-open")
.css("display","none")
.appendTo( $T )
//.hover( addHover, removeHover ) // use ui-layout-toggler-west-hover .content-open instead!
;
if (o.togglerContent_closed) // ui-layout-closed
$("<span>"+ o.togglerContent_closed +"</span>")
.data("layoutRole", "togglerContent")
.data("layoutEdge", pane)
.addClass("content content-closed")
.css("display","none")
.appendTo( $T )
//.hover( addHover, removeHover ) // use ui-layout-toggler-west-hover .content-closed instead!
;
// ADD TOGGLER.click/.hover
enableClosable(pane);
}
// add Draggable events
initResizable(pane);
// ADD CLASSNAMES & SLIDE-BINDINGS - eg: class="resizer resizer-west resizer-open"
if (s.isVisible)
setAsOpen(pane); // onOpen will be called, but NOT onResize
else {
setAsClosed(pane); // onClose will be called
bindStartSlidingEvent(pane, true); // will enable events IF option is set
}
});
// SET ALL HANDLE DIMENSIONS
sizeHandles("all");
};
/**
* Initialize scrolling ui-layout-content div - if exists
*
* @see initPane() - or externally after an Ajax injection
* @param {string} pane The pane to process
* @param {boolean=} resize Size content after init, default = true
*/
var initContent = function (pane, resize) {
var
o = options[pane]
, sel = o.contentSelector
, $P = $Ps[pane]
, $C
;
if (sel) $C = $Cs[pane] = (o.findNestedContent)
? $P.find(sel).eq(0) // match 1-element only
: $P.children(sel).eq(0)
;
if ($C && $C.length) {
// SAVE original Pane CSS
if (!$C.data("layoutCSS"))
$C.data("layoutCSS", getElemCSS($C, "height"));
$C.css( _c.content.cssReq );
if (o.applyDemoStyles) {
$C.css( _c.content.cssDemo ); // add padding & overflow: auto to content-div
$P.css( _c.content.cssDemoPane ); // REMOVE padding/scrolling from pane
}
state[pane].content = {}; // init content state
if (resize !== false) sizeContent(pane);
// sizeContent() is called AFTER init of all elements
}
else
$Cs[pane] = false;
};
/**
* Searches for .ui-layout-button-xxx elements and auto-binds them as layout-buttons
*
* @see _create()
*/
var initButtons = function () {
var pre = "ui-layout-button-", name;
$.each("toggle,open,close,pin,toggle-slide,open-slide".split(","), function (i, action) {
$.each(_c.borderPanes.split(","), function (ii, pane) {
$("."+pre+action+"-"+pane).each(function(){
// if button was previously 'bound', data.layoutName was set, but is blank if layout has no 'name'
name = $(this).data("layoutName") || $(this).attr("layoutName");
if (name == undefined || name == options.name)
bindButton(this, action, pane);
});
});
});
};
/**
* Add resize-bars to all panes that specify it in options
* -dependancy: $.fn.resizable - will skip if not found
*
* @see _create()
* @param {string=} panes The edge(s) to process, blank = all
*/
var initResizable = function (panes) {
var
draggingAvailable = (typeof $.fn.draggable == "function")
, $Frames, side // set in start()
;
if (!panes || panes == "all") panes = _c.borderPanes;
$.each(panes.split(","), function (idx, pane) {
var
o = options[pane]
, s = state[pane]
, c = _c[pane]
, side = (c.dir=="horz" ? "top" : "left")
, r, live // set in start because may change
;
if (!draggingAvailable || !$Ps[pane] || !o.resizable) {
o.resizable = false;
return true; // skip to next
}
var
$P = $Ps[pane]
, $R = $Rs[pane]
, base = o.resizerClass
// 'drag' classes are applied to the ORIGINAL resizer-bar while dragging is in process
, resizerClass = base+"-drag" // resizer-drag
, resizerPaneClass = base+"-"+pane+"-drag" // resizer-north-drag
// 'helper' class is applied to the CLONED resizer-bar while it is being dragged
, helperClass = base+"-dragging" // resizer-dragging
, helperPaneClass = base+"-"+pane+"-dragging" // resizer-north-dragging
, helperLimitClass = base+"-dragging-limit" // resizer-drag
, helperPaneLimitClass = base+"-"+pane+"-dragging-limit" // resizer-north-drag
, helperClassesSet = false // logic var
;
if (!s.isClosed)
$R
.attr("title", o.resizerTip)
.css("cursor", o.resizerCursor) // n-resize, s-resize, etc
;
$R.bind("mouseenter."+ sID, onResizerEnter)
.bind("mouseleave."+ sID, onResizerLeave);
$R.draggable({
containment: $Container[0] // limit resizing to layout container
, axis: (c.dir=="horz" ? "y" : "x") // limit resizing to horz or vert axis
, delay: 0
, distance: 1
// basic format for helper - style it using class: .ui-draggable-dragging
, helper: "clone"
, opacity: o.resizerDragOpacity
, addClasses: false // avoid ui-state-disabled class when disabled
//, iframeFix: o.draggableIframeFix // TODO: consider using when bug is fixed
, zIndex: _c.zIndex.resizer_drag
, start: function (e, ui) {
// REFRESH options & state pointers in case we used swapPanes
o = options[pane];
s = state[pane];
// re-read options
live = o.resizeWhileDragging;
// ondrag_start callback - will CANCEL hide if returns false
// TODO: dragging CANNOT be cancelled like this, so see if there is a way?
if (false === _execCallback(pane, o.ondrag_start)) return false;
_c.isLayoutBusy = true; // used by sizePane() logic during a liveResize
s.isResizing = true; // prevent pane from closing while resizing
timer.clear(pane+"_closeSlider"); // just in case already triggered
// SET RESIZER LIMITS - used in drag()
setSizeLimits(pane); // update pane/resizer state
r = s.resizerPosition;
$R.addClass( resizerClass +" "+ resizerPaneClass ); // add drag classes
helperClassesSet = false; // reset logic var - see drag()
// MASK PANES WITH IFRAMES OR OTHER TROUBLESOME ELEMENTS
$Frames = $(o.maskIframesOnResize === true ? "iframe" : o.maskIframesOnResize).filter(":visible");
var id, i=0; // ID incrementer - used when 'resizing' masks during dynamic resizing
$Frames.each(function() {
id = "ui-layout-mask-"+ (++i);
$(this).data("layoutMaskID", id); // tag iframe with corresponding maskID
$('<div id="'+ id +'" class="ui-layout-mask ui-layout-mask-'+ pane +'"/>')
.css({
background: "#fff"
, opacity: "0.001"
, zIndex: _c.zIndex.iframe_mask
, position: "absolute"
, width: this.offsetWidth+"px"
, height: this.offsetHeight+"px"
})
.css($(this).position()) // top & left -- changed from offset()
.appendTo(this.parentNode) // put mask-div INSIDE pane to avoid zIndex issues
;
});
// DISABLE TEXT SELECTION (probably already done by resizer.mouseOver)
$('body').disableSelection();
}
, drag: function (e, ui) {
if (!helperClassesSet) { // can only add classes after clone has been added to the DOM
//$(".ui-draggable-dragging")
ui.helper
.addClass( helperClass +" "+ helperPaneClass ) // add helper classes
.css({ right: "auto", bottom: "auto" }) // fix dir="rtl" issue
.children().css("visibility","hidden") // hide toggler inside dragged resizer-bar
;
helperClassesSet = 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.pane_sliding);
}
// CONTAIN RESIZER-BAR TO RESIZING LIMITS
var limit = 0;
if (ui.position[side] < r.min) {
ui.position[side] = r.min;
limit = -1;
}
else if (ui.position[side] > r.max) {
ui.position[side] = r.max;
limit = 1;
}
// ADD/REMOVE dragging-limit CLASS
if (limit) {
ui.helper.addClass( helperLimitClass +" "+ helperPaneLimitClass ); // at dragging-limit
window.defaultStatus = "Panel has reached its " +
((limit>0 && pane.match(/north|west/)) || (limit<0 && pane.match(/south|east/)) ? "maximum" : "minimum") +" size";
}
else {
ui.helper.removeClass( helperLimitClass +" "+ helperPaneLimitClass ); // not at dragging-limit
window.defaultStatus = "";
}
// DYNAMICALLY RESIZE PANES IF OPTION ENABLED
if (live) resizePanes(e, ui, pane);
}
, stop: function (e, ui) {
$('body').enableSelection(); // RE-ENABLE TEXT SELECTION
window.defaultStatus = ""; // clear 'resizing limit' message from statusbar
$R.removeClass( resizerClass +" "+ resizerPaneClass ); // remove drag classes from Resizer
s.isResizing = false;
_c.isLayoutBusy = false; // set BEFORE resizePanes so other logic can pick it up
resizePanes(e, ui, pane, true); // true = resizingDone
}
});
/**
* resizePanes
*
* Sub-routine called from stop() and optionally drag()
*
* @param {!Object} evt
* @param {!Object} ui
* @param {string} pane
* @param {boolean=} resizingDone
*/
var resizePanes = function (evt, ui, pane, resizingDone) {
var
dragPos = ui.position
, c = _c[pane]
, resizerPos, newSize
, i = 0 // ID incrementer
;
switch (pane) {
case "north": resizerPos = dragPos.top; break;
case "west": resizerPos = dragPos.left; break;
case "south": resizerPos = sC.offsetHeight - dragPos.top - o.spacing_open; break;
case "east": resizerPos = sC.offsetWidth - dragPos.left - o.spacing_open; break;
};
if (resizingDone) {
// Remove OR Resize MASK(S) created in drag.start
$("div.ui-layout-mask").each(function() { this.parentNode.removeChild(this); });
//$("div.ui-layout-mask").remove(); // TODO: Is this less efficient?
// ondrag_start callback - will CANCEL hide if returns false
if (false === _execCallback(pane, o.ondrag_end || o.ondrag)) return false;
}
else
$Frames.each(function() {
$("#"+ $(this).data("layoutMaskID")) // get corresponding mask by ID
.css($(this).position()) // update top & left
.css({ // update width & height
width: this.offsetWidth +"px"
, height: this.offsetHeight+"px"
})
;
});
// remove container margin from resizer position to get the pane size
newSize = resizerPos - sC["inset"+ c.side];
manualSizePane(pane, newSize);
}
});
};
/**
* Destroy this layout and reset all elements
*/
var destroy = function () {
// UNBIND layout events and remove global object
$(window).unbind("."+ sID);
$(document).unbind("."+ sID);
// loop all panes to remove layout classes, attributes and bindings
$.each(_c.allPanes.split(","), function (i, pane) {
removePane( pane, false, true ); // true = skipResize
});
// reset layout-container
var $C = $Container
.removeData("layout")
.removeData("layoutContainer")
.removeClass(options.containerClass)
;
// do NOT reset container CSS if is a 'pane' in an outer-layout - ie, THIS layout is 'nested'
if (!$C.data("layoutEdge") && $C.data("layoutCSS")) // RESET CSS
$C.css( $C.data("layoutCSS") ).removeData("layoutCSS");
// for full-page layouts, also reset the <HTML> CSS
if (sC.tagName == "BODY" && ($C = $("html")).data("layoutCSS")) // RESET <HTML> CSS
$C.css( $C.data("layoutCSS") ).removeData("layoutCSS");
// trigger state-management and onunload callback
unload();
};
/**
* Remove a pane from the layout - subroutine of destroy()
*
* @see destroy()
* @param {string} pane The pane to process
* @param {boolean=} remove Remove the DOM element? default = false
* @param {boolean=} skipResize Skip calling resizeAll()? default = false
*/
var removePane = function (pane, remove, skipResize) {
if (!$Ps[pane]) return; // NO SUCH PANE
var
$P = $Ps[pane]
, $C = $Cs[pane]
, $R = $Rs[pane]
, $T = $Ts[pane]
// create list of ALL pane-classes that need to be removed
, _open = "-open"
, _sliding= "-sliding"
, _closed = "-closed"
, root = options[pane].paneClass // default="ui-layout-pane"
, pRoot = root +"-"+ pane // eg: "ui-layout-pane-west"
, classes = [ root, root+_open, root+_closed, root+_sliding, // generic classes
pRoot, pRoot+_open, pRoot+_closed, pRoot+_sliding ] // pane-specific classes
;
$.merge(classes, getHoverClasses($P, true)); // ADD hover-classes
if (!$P || !$P.length) {
} // pane has already been deleted!
else if (remove && !$P.data("layoutContainer") && (!$C || !$C.length || !$C.data("layoutContainer")))
$P.remove();
else {
$P .removeClass( classes.join(" ") ) // remove ALL pane-classes
.removeData("layoutParent")
.removeData("layoutRole")
.removeData("layoutEdge")
.removeData("autoHidden") // in case set
.unbind("."+ sID) // remove ALL Layout events
// TODO: remove these extra unbind commands when jQuery is fixed
//.unbind("mouseenter"+ sID)
//.unbind("mouseleave"+ sID)
;
// do NOT reset CSS if this pane is STILL the container of a nested layout!
// the nested layout will reset its 'container' when/if it is destroyed
if (!$P.data("layoutContainer"))
$P.css( $P.data("layoutCSS") ).removeData("layoutCSS");
// DITTO for the Content elem
if ($C && $C.length && !$C.data("layoutContainer"))
$C.css( $C.data("layoutCSS") ).removeData("layoutCSS");
}
// REMOVE pane's resizer and toggler elements
if ($T && $T.length) $T.remove();
if ($R && $R.length) $R.remove();
// CLEAR all pointers and data
$Ps[pane] = $Cs[pane] = $Rs[pane] = $Ts[pane] = false;
// skip resize & state-clear when called from destroy()
if (!skipResize) {
resizeAll();
state[pane] = {};
}
};
/*
* ###########################
* ACTION METHODS
* ###########################
*/
/**
* 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
* @param {boolean=} noAnimation
*/
var hide = function (pane, noAnimation) {
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 (state.initialized && false === _execCallback(pane, o.onhide_start)) return;
s.isSliding = false; // just in case
// now hide the elements
if ($R) $R.hide(); // hide resizer-bar
if (!state.initialized || s.isClosed) {
s.isClosed = true; // to trigger open-animation on show()
s.isHidden = true;
s.isVisible = false;
$P.hide(); // no animation when loading page
sizeMidPanes(_c[pane].dir == "horz" ? "all" : "center");
if (state.initialized || o.triggerEventsOnLoad)
_execCallback(pane, o.onhide_end || o.onhide);
}
else {
s.isHiding = true; // used by onclose
close(pane, false, noAnimation); // adjust all panes to fit
}
};
/**
* Show a hidden pane - show as 'closed' by default unless openPane = true
*
* @param {string} pane The pane being opened, ie: north, south, east, or west
* @param {boolean=} openPane
* @param {boolean=} noAnimation
* @param {boolean=} noAlert
*/
var show = function (pane, openPane, noAnimation, noAlert) {
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
// onshow_start callback - will CANCEL show if returns false
if (false === _execCallback(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) $R.show(); - will be shown by open/close
if (openPane === false)
close(pane, true); // true = force
else
open(pane, false, noAnimation, noAlert); // adjust all panes to fit
};
/**
* Toggles a pane open/closed by calling either open or close
*
* @param {string} pane The pane being toggled, ie: north, south, east, or west
* @param {boolean=} slide
*/
var toggle = function (pane, slide) {
if (!isStr(pane)) {
pane.stopImmediatePropagation(); // pane = event
pane = $(this).data("layoutEdge"); // bound to $R.dblclick
}
var s = state[str(pane)];
if (s.isHidden)
show(pane); // will call 'open' after unhiding it
else if (s.isClosed)
open(pane, !!slide);
else
close(pane);
};
/**
* Utility method used during init or other auto-processes
*
* @param {string} pane The pane being closed
* @param {boolean=} setHandles
*/
var _closePane = function (pane, setHandles) {
var
$P = $Ps[pane]
, s = state[pane]
;
$P.hide();
s.isClosed = true;
s.isVisible = false;
// UNUSED: if (setHandles) setAsClosed(pane, true); // true = force
};
/**
* 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
* @param {boolean=} force
* @param {boolean=} noAnimation
* @param {boolean=} skipCallback
*/
var close = function (pane, force, noAnimation, skipCallback) {
if (!state.initialized) {
_closePane(pane)
return;
}
var
$P = $Ps[pane]
, $R = $Rs[pane]
, $T = $Ts[pane]
, o = options[pane]
, s = state[pane]
, doFX = !noAnimation && !s.isClosed && (o.fxName_close != "none")
// transfer logic vars to temp vars
, isShowing = s.isShowing
, isHiding = s.isHiding
, wasSliding = s.isSliding
;
// now clear the logic vars
delete s.isShowing;
delete s.isHiding;
if (!$P || (!o.closable && !isShowing && !isHiding)) return; // invalid request // (!o.resizable && !o.closable) ???
else if (!force && s.isClosed && !isShowing) return; // already closed
if (_c.isLayoutBusy) { // layout is 'busy' - probably with an animation
_queue("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 === _execCallback(pane, o.onclose_start)) return;
// SET flow-control flags
_c[pane].isMoving = true;
_c.isLayoutBusy = true;
s.isClosed = true;
s.isVisible = false;
// update isHidden BEFORE sizing panes
if (isHiding) s.isHidden = true;
else if (isShowing) s.isHidden = false;
if (s.isSliding) // pane is being closed, so UNBIND trigger events
bindStopSlidingEvents(pane, false); // will set isSliding=false
else // resize panes adjacent to this one
sizeMidPanes(_c[pane].dir == "horz" ? "all" : "center", false); // false = NOT skipCallback
// if this pane has a resizer bar, move it NOW - before animation
setAsClosed(pane);
// CLOSE THE PANE
if (doFX) { // animate the close
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
close_2();
});
}
else { // hide the pane without animation
$P.hide();
close_2();
};
// SUBROUTINE
function close_2 () {
if (s.isClosed) { // make sure pane was not 'reopened' before animation finished!
bindStartSlidingEvent(pane, true); // will enable if o.slidable = true
// if opposite-pane was autoClosed, see if it can be autoOpened now
var altPane = _c.altSide[pane];
if (state[ altPane ].noRoom) {
setSizeLimits( altPane );
makePaneFit( altPane );
}
if (!skipCallback && (state.initialized || o.triggerEventsOnLoad)) {
// onclose callback - UNLESS just 'showing' a hidden pane as 'closed'
if (!isShowing) _execCallback(pane, o.onclose_end || o.onclose);
// onhide OR onshow callback
if (isShowing) _execCallback(pane, o.onshow_end || o.onshow);
if (isHiding) _execCallback(pane, o.onhide_end || o.onhide);
}
}
// execute internal flow-control callback
_dequeue(pane);
}
};
/**
* @param {string} pane The pane just closed, ie: north, south, east, or west
*/
var setAsClosed = function (pane) {
var
$P = $Ps[pane]
, $R = $Rs[pane]
, $T = $Ts[pane]
, o = options[pane]
, s = state[pane]
, side = _c[pane].side.toLowerCase()
, inset = "inset"+ _c[pane].side
, rClass = o.resizerClass
, tClass = o.togglerClass
, _pane = "-"+ pane // used for classNames
, _open = "-open"
, _sliding= "-sliding"
, _closed = "-closed"
;
$R
.css(side, sC[inset]) // move the resizer
.removeClass( rClass+_open +" "+ rClass+_pane+_open )
.removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
.addClass( rClass+_closed +" "+ rClass+_pane+_closed )
.unbind("dblclick."+ sID)
;
// DISABLE 'resizing' when closed - do this BEFORE bindStartSlidingEvent?
if (o.resizable && typeof $.fn.draggable == "function")
$R
.draggable("disable")
.removeClass("ui-state-disabled") // do NOT apply disabled styling - not suitable here
.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
;
// toggler-content - if exists
$T.children(".content-open").hide();
$T.children(".content-closed").css("display","block");
}
// sync any 'pin buttons'
syncPinBtns(pane, false);
if (state.initialized) {
// resize 'length' and position togglers for adjacent panes
sizeHandles("all");
}
};
/**
* 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
* @param {boolean=} slide
* @param {boolean=} noAnimation
* @param {boolean=} noAlert
*/
var open = function (pane, slide, noAnimation, noAlert) {
var
$P = $Ps[pane]
, $R = $Rs[pane]
, $T = $Ts[pane]
, o = options[pane]
, s = state[pane]
, doFX = !noAnimation && s.isClosed && (o.fxName_open != "none")
// transfer logic var to temp var
, isShowing = s.isShowing
;
// now clear the logic var
delete s.isShowing;
if (!$P || (!o.resizable && !o.closable && !isShowing)) return; // invalid request
else if (s.isVisible && !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
_queue("open", pane, slide); // set a callback for this action, if possible
return; // ABORT
}
setSizeLimits(pane, slide); // update pane-state
// onopen_start callback - will CANCEL hide if returns false
if (false === _execCallback(pane, o.onopen_start)) return;
// make sure there is enough space available to open the pane
if (s.minSize > s.maxSize) { // INSUFFICIENT ROOM FOR PANE TO OPEN!
syncPinBtns(pane, false); // make sure pin-buttons are reset
if (!noAlert && o.noRoomToOpenTip) alert(o.noRoomToOpenTip);
return; // ABORT
}
// SET flow-control flags
_c[pane].isMoving = true;
_c.isLayoutBusy = true;
if (slide) // START Sliding - will set isSliding=true
bindStopSlidingEvents(pane, true); // BIND trigger events to close sliding-pane
else if (s.isSliding) // PIN PANE (stop sliding) - open pane 'normally' instead
bindStopSlidingEvents(pane, false); // UNBIND trigger events - will set isSliding=false
else if (o.slidable)
bindStartSlidingEvent(pane, false); // UNBIND trigger events
s.noRoom = false; // will be reset by makePaneFit if 'noRoom'
makePaneFit(pane);
s.isVisible = true;
s.isClosed = false;
// update isHidden BEFORE sizing panes - WHY??? Old?
if (isShowing) s.isHidden = false;
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
open_2(); // continue
});
}
else {// no animation
$P.show(); // just show pane and...
open_2(); // continue
};
// SUBROUTINE
function open_2 () {
if (s.isVisible) { // make sure pane was not closed or hidden before animation finished!
// cure iframe display issues
_fixIframe(pane);
// 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", false); // false = NOT skipCallback
// set classes, position handles and execute callbacks...
setAsOpen(pane);
}
// internal flow-control callback
_dequeue(pane);
};
};
/**
* @param {string} pane The pane just opened, ie: north, south, east, or west
* @param {boolean=} skipCallback
*/
var setAsOpen = function (pane, skipCallback) {
var
$P = $Ps[pane]
, $R = $Rs[pane]
, $T = $Ts[pane]
, o = options[pane]
, s = state[pane]
, side = _c[pane].side.toLowerCase()
, inset = "inset"+ _c[pane].side
, rClass = o.resizerClass
, tClass = o.togglerClass
, _pane = "-"+ pane // used for classNames
, _open = "-open"
, _closed = "-closed"
, _sliding= "-sliding"
;
$R
.css(side, sC[inset] + getPaneSize(pane)) // move the resizer
.removeClass( rClass+_closed +" "+ rClass+_pane+_closed )
.addClass( rClass+_open +" "+ rClass+_pane+_open )
;
if (s.isSliding)
$R.addClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
else // in case 'was sliding'
$R.removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
if (o.resizerDblClickToggle)
$R.bind("dblclick", toggle );
removeHover( 0, $R ); // remove hover classes
if (o.resizable && typeof $.fn.draggable == "function")
$R
.draggable("enable")
.css("cursor", o.resizerCursor)
.attr("title", o.resizerTip)
;
else if (!s.isSliding)
$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
;
removeHover( 0, $T ); // remove hover classes
// toggler-content - if exists
$T.children(".content-closed").hide();
$T.children(".content-open").css("display","block");
}
// sync any 'pin buttons'
syncPinBtns(pane, !s.isSliding);
// update pane-state dimensions - BEFORE resizing content
$.extend(s, getElemDims($P));
if (state.initialized) {
// resize resizer & toggler sizes for all panes
sizeHandles("all");
// resize content every time pane opens - to be sure
sizeContent(pane, true); // true = remeasure headers/footers, even if 'isLayoutBusy'
}
if (!skipCallback && (state.initialized || o.triggerEventsOnLoad) && $P.is(":visible")) {
// onopen callback
_execCallback(pane, o.onopen_end || o.onopen);
// onshow callback - TODO: should this be here?
if (s.isShowing) _execCallback(pane, o.onshow_end || o.onshow);
// ALSO call onresize because layout-size *may* have changed while pane was closed
if (state.initialized) {
_execCallback(pane, o.onresize_end || o.onresize);
resizeNestedLayout(pane);
}
}
};
/**
* slideOpen / slideClose / slideToggle
*
* Pass-though methods for sliding
*/
var slideOpen = function (evt_or_pane) {
var
evt = isStr(evt_or_pane) ? null : evt_or_pane
, pane = evt ? $(this).data("layoutEdge") : evt_or_pane
, s = state[pane]
, delay = options[pane].slideDelay_open
;
// prevent event from triggering on NEW resizer binding created below
if (evt) evt.stopImmediatePropagation();
if (s.isClosed && evt && evt.type == "mouseenter" && delay > 0)
// trigger = mouseenter - use a delay
timer.set(pane+"_openSlider", open_NOW, delay);
else
open_NOW(); // will unbind events if is already open
/**
* SUBROUTINE for timed open
*/
function open_NOW (evt) {
if (!s.isClosed) // skip if no longer closed!
bindStopSlidingEvents(pane, true); // BIND trigger events to close sliding-pane
else if (!_c[pane].isMoving)
open(pane, true); // true = slide - open() will handle binding
};
};
var slideClose = function (evt_or_pane) {
var
evt = isStr(evt_or_pane) ? null : evt_or_pane
, pane = evt ? $(this).data("layoutEdge") : evt_or_pane
, o = options[pane]
, s = state[pane]
, delay = _c[pane].isMoving ? 1000 : 300 // MINIMUM delay - option may override
;
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 if (o.preventQuickSlideClose && _c.isLayoutBusy)
return; // handle Chrome quick-close on slide-open
else if (o.preventPrematureSlideClose && evt && $.layout.isMouseOverElem(evt, $Ps[pane]))
return; // handle incorrect mouseleave trigger, like when over a SELECT-list in IE
else if (evt) // trigger = mouseleave - use a delay
// 1 sec delay if 'opening', else .3 sec
timer.set(pane+"_closeSlider", close_NOW, max(o.slideDelay_close, delay));
else // called programically
close_NOW();
/**
* SUBROUTINE for timed close
*/
function close_NOW () {
if (s.isClosed) // skip 'close' if already closed!
bindStopSlidingEvents(pane, false); // UNBIND trigger events - TODO: is this needed here?
else if (!_c[pane].isMoving)
close(pane); // close will handle unbinding
};
};
var slideToggle = function (pane) { toggle(pane, true); };
/**
* 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.pane_animate }); // overlay all elements during animation
if (pane=="south")
$P.css({ top: sC.insetTop + sC.innerHeight - $P.outerHeight() });
else if (pane=="east")
$P.css({ left: sC.insetLeft + sC.innerWidth - $P.outerWidth() });
}
else { // animation DONE - RESET CSS
// TODO: see if this can be deleted. It causes a quick-close when sliding in Chrome
$P.css({ zIndex: (state[pane].isSliding ? _c.zIndex.pane_sliding : _c.zIndex.pane_normal) });
if (pane=="south")
$P.css({ top: "auto" });
else if (pane=="east")
$P.css({ left: "auto" });
// fix anti-aliasing in IE - only needed for animations that change opacity
var o = options[pane];
if (state.browser.msie && o.fxOpacityFix && o.fxName_open != "slide" && $P.css("filter") && $P.css("opacity") == 1)
$P[0].style.removeAttribute('filter');
}
};
/**
* Toggle sliding functionality of a specific pane on/off by adding removing 'slide open' trigger
*
* @see 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]
, $P = $Ps[pane]
, $R = $Rs[pane]
, trigger = o.slideTrigger_open.toLowerCase()
;
if (!$R || (enable && !o.slidable)) return;
// make sure we have a valid event
if (trigger.match(/mouseover/))
trigger = o.slideTrigger_open = "mouseenter";
else if (!trigger.match(/click|dblclick|mouseenter/))
trigger = o.slideTrigger_open = "click";
$R
// add or remove trigger event
[enable ? "bind" : "unbind"](trigger +'.'+ sID, slideOpen)
// set the appropriate cursor & title/tip
.css("cursor", enable ? o.sliderCursor : "default")
.attr("title", enable ? o.sliderTip : "")
;
};
/**
* Add or remove 'mouseleave' 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'
*
* @see slideOpen(), slideClose()
* @param {string} pane The pane to process, 'north', 'south', etc.
* @param {boolean} enable Enable or Disable events?
*/
var bindStopSlidingEvents = function (pane, enable) {
var
o = options[pane]
, s = state[pane]
, z = _c.zIndex
, trigger = o.slideTrigger_close.toLowerCase()
, action = (enable ? "bind" : "unbind")
, $P = $Ps[pane]
, $R = $Rs[pane]
;
s.isSliding = enable; // logic
timer.clear(pane+"_closeSlider"); // just in case
// remove 'slideOpen' trigger event from resizer
// ALSO will raise the zIndex of the pane & resizer
if (enable) bindStartSlidingEvent(pane, false);
// RE/SET zIndex - increases when pane is sliding-open, resets to normal when not
$P.css("zIndex", enable ? z.pane_sliding : z.pane_normal);
$R.css("zIndex", enable ? z.pane_sliding : z.resizer_normal);
// make sure we have a valid event
if (!trigger.match(/click|mouseleave/))
trigger = o.slideTrigger_close = "mouseleave"; // also catches 'mouseout'
// add/remove slide triggers
$R[action](trigger, slideClose); // base event on resize
// need extra events for mouseleave
if (trigger == "mouseleave") {
// also close on pane.mouseleave
$P[action]("mouseleave."+ sID, slideClose);
// cancel timer when mouse moves between 'pane' and 'resizer'
$R[action]("mouseenter."+ sID, cancelMouseOut);
$P[action]("mouseenter."+ sID, cancelMouseOut);
}
if (!enable)
timer.clear(pane+"_closeSlider");
else if (trigger == "click" && !o.resizable) {
// IF pane is not resizable (which already has a cursor and tip)
// then set the a cursor & title/tip on resizer when sliding
$R.css("cursor", enable ? o.sliderCursor : "default");
$R.attr("title", enable ? o.togglerTip_open : ""); // use Toggler-tip, eg: "Close Pane"
}
// SUBROUTINE for mouseleave timer clearing
function cancelMouseOut (evt) {
timer.clear(pane+"_closeSlider");
evt.stopPropagation();
}
};
/**
* Hides/closes a pane if there is insufficient room - reverses this when there is room again
* MUST have already called setSizeLimits() before calling this method
*
* @param {string} pane The pane being resized
* @param {boolean=} isOpening Called from onOpen?
* @param {boolean=} skipCallback Should the onresize callback be run?
* @param {boolean=} force
*/
var makePaneFit = function (pane, isOpening, skipCallback, force) {
var
o = options[pane]
, s = state[pane]
, c = _c[pane]
, $P = $Ps[pane]
, $R = $Rs[pane]
, isSidePane = c.dir=="vert"
, hasRoom = false
;
// special handling for center & east/west panes
if (pane == "center" || (isSidePane && s.noVerticalRoom)) {
// see if there is enough room to display the pane
// ERROR: hasRoom = s.minHeight <= s.maxHeight && (isSidePane || s.minWidth <= s.maxWidth);
hasRoom = (s.maxHeight > 0);
if (hasRoom && s.noRoom) { // previously hidden due to noRoom, so show now
$P.show();
if ($R) $R.show();
s.isVisible = true;
s.noRoom = false;
if (isSidePane) s.noVerticalRoom = false;
_fixIframe(pane);
}
else if (!hasRoom && !s.noRoom) { // not currently hidden, so hide now
$P.hide();
if ($R) $R.hide();
s.isVisible = false;
s.noRoom = true;
}
}
// see if there is enough room to fit the border-pane
if (pane == "center") {
// ignore center in this block
}
else if (s.minSize <= s.maxSize) { // pane CAN fit
hasRoom = true;
if (s.size > s.maxSize) // pane is too big - shrink it
sizePane(pane, s.maxSize, skipCallback, force);
else if (s.size < s.minSize) // pane is too small - enlarge it
sizePane(pane, s.minSize, skipCallback, force);
else if ($R && $P.is(":visible")) {
// make sure resizer-bar is positioned correctly
// handles situation where nested layout was 'hidden' when initialized
var
side = c.side.toLowerCase()
, pos = s.size + sC["inset"+ c.side]
;
if (_cssNum($R, side) != pos) $R.css( side, pos );
}
// if was previously hidden due to noRoom, then RESET because NOW there is room
if (s.noRoom) {
// s.noRoom state will be set by open or show
if (s.wasOpen && o.closable) {
if (o.autoReopen)
open(pane, false, true, true); // true = noAnimation, true = noAlert
else // leave the pane closed, so just update state
s.noRoom = false;
}
else
show(pane, s.wasOpen, true, true); // true = noAnimation, true = noAlert
}
}
else { // !hasRoom - pane CANNOT fit
if (!s.noRoom) { // pane not set as noRoom yet, so hide or close it now...
s.noRoom = true; // update state
s.wasOpen = !s.isClosed && !s.isSliding;
if (s.isClosed){} // SKIP
else if (o.closable) // 'close' if possible
close(pane, true, true); // true = force, true = noAnimation
else // 'hide' pane if cannot just be closed
hide(pane, true); // true = noAnimation
}
}
};
/**
* sizePane / manualSizePane
* sizePane is called only by internal methods whenever a pane needs to be resized
* manualSizePane is an exposed flow-through method allowing extra code when pane is 'manually resized'
*
* @param {string} pane The pane being resized
* @param {number} size The *desired* new size for this pane - will be validated
* @param {boolean=} skipCallback Should the onresize callback be run?
*/
var manualSizePane = function (pane, size, skipCallback) {
// ANY call to sizePane will disabled autoResize
var
o = options[pane]
// if resizing callbacks have been delayed and resizing is now DONE, force resizing to complete...
, forceResize = o.resizeWhileDragging && !_c.isLayoutBusy // && !o.triggerEventsWhileDragging
;
o.autoResize = false;
// flow-through...
sizePane(pane, size, skipCallback, forceResize);
}
/**
* @param {string} pane The pane being resized
* @param {number} size The *desired* new size for this pane - will be validated
* @param {boolean=} skipCallback Should the onresize callback be run?
* @param {boolean=} force Force resizing even if does not seem necessary
*/
var sizePane = function (pane, size, skipCallback, force) {
var
o = options[pane]
, s = state[pane]
, $P = $Ps[pane]
, $R = $Rs[pane]
, side = _c[pane].side.toLowerCase()
, inset = "inset"+ _c[pane].side
, skipResizeWhileDragging = _c.isLayoutBusy && !o.triggerEventsWhileDragging
, oldSize
;
// calculate 'current' min/max sizes
setSizeLimits(pane); // update pane-state
oldSize = s.size;
size = _parseSize(pane, size); // handle percentages & auto
size = max(size, _parseSize(pane, o.minSize));
size = min(size, s.maxSize);
if (size < s.minSize) { // not enough room for pane!
makePaneFit(pane, false, skipCallback); // will hide or close pane
return;
}
// IF newSize is same as oldSize, then nothing to do - abort
if (!force && size == oldSize) return;
// onresize_start callback CANNOT cancel resizing because this would break the layout!
if (!skipCallback && state.initialized && s.isVisible)
_execCallback(pane, o.onresize_start);
// resize the pane, and make sure its visible
$P.css( _c[pane].sizeType.toLowerCase(), max(1, cssSize(pane, size)) );
// update pane-state dimensions
s.size = size;
$.extend(s, getElemDims($P));
// reposition the resizer-bar
if ($R && $P.is(":visible")) $R.css( side, size + sC[inset] );
sizeContent(pane);
if (!skipCallback && !skipResizeWhileDragging && state.initialized && s.isVisible) {
_execCallback(pane, o.onresize_end || o.onresize);
resizeNestedLayout(pane);
}
// resize all the adjacent panes, and adjust their toggler buttons
// when skipCallback passed, it means the controlling method will handle 'other panes'
if (!skipCallback) {
// also no callback if live-resize is in progress and NOT triggerEventsWhileDragging
if (!s.isSliding) sizeMidPanes(_c[pane].dir=="horz" ? "all" : "center", skipResizeWhileDragging, force);
sizeHandles("all");
}
// if opposite-pane was autoClosed, see if it can be autoOpened now
var altPane = _c.altSide[pane];
if (size < oldSize && state[ altPane ].noRoom) {
setSizeLimits( altPane );
makePaneFit( altPane, false, skipCallback );
}
};
/**
* @see initPanes(), sizePane(), resizeAll(), open(), close(), hide()
* @param {string} panes The pane(s) being resized, comma-delmited string
* @param {boolean=} skipCallback Should the onresize callback be run?
* @param {boolean=} force
*/
var sizeMidPanes = function (panes, skipCallback, force) {
if (!panes || panes == "all") panes = "east,west,center";
$.each(panes.split(","), function (i, pane) {
if (!$Ps[pane]) return; // NO PANE - skip
var
o = options[pane]
, s = state[pane]
, $P = $Ps[pane]
, $R = $Rs[pane]
, isCenter= (pane=="center")
, hasRoom = true
, CSS = {}
, d = calcNewCenterPaneDims()
;
// update pane-state dimensions
$.extend(s, getElemDims($P));
if (pane == "center") {
if (!force && s.isVisible && d.width == s.outerWidth && d.height == s.outerHeight)
return true; // SKIP - pane already the correct size
// set state for makePaneFit() logic
$.extend(s, cssMinDims(pane), {
maxWidth: d.width
, maxHeight: d.height
});
CSS = d;
// convert OUTER width/height to CSS width/height
CSS.width = cssW(pane, d.width);
CSS.height = cssH(pane, d.height);
hasRoom = CSS.width > 0 && CSS.height > 0;
// during layout init, try to shrink east/west panes to make room for center
if (!hasRoom && !state.initialized && o.minWidth > 0) {
var
reqPx = o.minWidth - s.outerWidth
, minE = options.east.minSize || 0
, minW = options.west.minSize || 0
, sizeE = state.east.size
, sizeW = state.west.size
, newE = sizeE
, newW = sizeW
;
if (reqPx > 0 && state.east.isVisible && sizeE > minE) {
newE = max( sizeE-minE, sizeE-reqPx );
reqPx -= sizeE-newE;
}
if (reqPx > 0 && state.west.isVisible && sizeW > minW) {
newW = max( sizeW-minW, sizeW-reqPx );
reqPx -= sizeW-newW;
}
// IF we found enough extra space, then resize the border panes as calculated
if (reqPx == 0) {
if (sizeE != minE)
sizePane('east', newE, true); // true = skipCallback - initPanes will handle when done
if (sizeW != minW)
sizePane('west', newW, true);
// now start over!
sizeMidPanes('center', skipCallback, force);
return; // abort this loop
}
}
}
else { // for east and west, set only the height, which is same as center height
// set state.min/maxWidth/Height for makePaneFit() logic
if (s.isVisible && !s.noVerticalRoom)
$.extend(s, getElemDims($P), cssMinDims(pane))
if (!force && !s.noVerticalRoom && d.height == s.outerHeight)
return true; // SKIP - pane already the correct size
CSS.top = d.top;
CSS.bottom = d.bottom;
CSS.height = cssH(pane, d.height);
s.maxHeight = max(0, CSS.height);
hasRoom = (s.maxHeight > 0);
if (!hasRoom) s.noVerticalRoom = true; // makePaneFit() logic
}
if (hasRoom) {
// resizeAll passes skipCallback because it triggers callbacks after ALL panes are resized
if (!skipCallback && state.initialized)
_execCallback(pane, o.onresize_start);
$P.css(CSS); // apply the CSS to pane
if (s.noRoom && !s.isClosed && !s.isHidden)
makePaneFit(pane); // will re-open/show auto-closed/hidden pane
if (s.isVisible) {
$.extend(s, getElemDims($P)); // update pane dimensions
if (state.initialized) sizeContent(pane); // also resize the contents, if exists
}
}
else if (!s.noRoom && s.isVisible) // no room for pane
makePaneFit(pane); // will hide or close pane
if (!s.isVisible)
return true; // DONE - next pane
/*
* 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
* ALSO required when pane is an IFRAME because will NOT default to 'full width'
*/
if (pane == "center") { // finished processing midPanes
var b = state.browser;
var fix = b.isIE6 || (b.msie && !b.boxModel);
if ($Ps.north && (fix || state.north.tagName=="IFRAME"))
$Ps.north.css("width", cssW($Ps.north, sC.innerWidth));
if ($Ps.south && (fix || state.south.tagName=="IFRAME"))
$Ps.south.css("width", cssW($Ps.south, sC.innerWidth));
}
// resizeAll passes skipCallback because it triggers callbacks after ALL panes are resized
if (!skipCallback && state.initialized) {
_execCallback(pane, o.onresize_end || o.onresize);
resizeNestedLayout(pane);
}
});
};
/**
* @see window.onresize(), callbacks or custom code
*/
var resizeAll = function () {
var
oldW = sC.innerWidth
, oldH = sC.innerHeight
;
$.extend( state.container, getElemDims( $Container ) ); // UPDATE container dimensions
if (!sC.outerHeight) return; // cannot size layout when 'container' is hidden or collapsed
// onresizeall_start will CANCEL resizing if returns false
// state.container has already been set, so user can access this info for calcuations
if (false === _execCallback(null, options.onresizeall_start)) return false;
var
// see if container is now 'smaller' than before
shrunkH = (sC.innerHeight < oldH)
, shrunkW = (sC.innerWidth < oldW)
, $P, o, s, dir
;
// NOTE special order for sizing: S-N-E-W
$.each(["south","north","east","west"], function (i, pane) {
if (!$Ps[pane]) return; // no pane - SKIP
s = state[pane];
o = options[pane];
dir = _c[pane].dir;
if (o.autoResize && s.size != o.size) // resize pane to original size set in options
sizePane(pane, o.size, true, true); // true=skipCallback, true=forceResize
else {
setSizeLimits(pane);
makePaneFit(pane, false, true, true); // true=skipCallback, true=forceResize
}
});
sizeMidPanes("all", true, true); // true=skipCallback, true=forceResize
sizeHandles("all"); // reposition the toggler elements
// trigger all individual pane callbacks AFTER layout has finished resizing
o = options; // reuse alias
$.each(_c.allPanes.split(","), function (i, pane) {
$P = $Ps[pane];
if (!$P) return; // SKIP
if (state[pane].isVisible) { // undefined for non-existent panes
_execCallback(pane, o[pane].onresize_end || o[pane].onresize); // callback - if exists
resizeNestedLayout(pane);
}
});
_execCallback(null, o.onresizeall_end || o.onresizeall); // onresizeall callback, if exists
};
/**
* Whenever a pane resizes or opens that has a nested layout, trigger resizeAll
*
* @param {string} pane The pane just resized or opened
*/
var resizeNestedLayout = function (pane) {
var
$P = $Ps[pane]
, $C = $Cs[pane]
, d = "layoutContainer"
;
if (options[pane].resizeNestedLayout) {
if ($P.data( d ))
$P.layout().resizeAll();
else if ($C && $C.data( d ))
$C.layout().resizeAll();
}
};
/**
* IF pane has a content-div, then resize all elements inside pane to fit pane-height
*
* @param {string=} panes The pane(s) being resized
* @param {boolean=} remeasure Should the content (header/footer) be remeasured?
*/
var sizeContent = function (panes, remeasure) {
if (!panes || panes == "all") panes = _c.allPanes;
$.each(panes.split(","), function (idx, pane) {
var
$P = $Ps[pane]
, $C = $Cs[pane]
, o = options[pane]
, s = state[pane]
, m = s.content // m = measurements
;
if (!$P || !$C || !$P.is(":visible")) return true; // NOT VISIBLE - skip
// onsizecontent_start will CANCEL resizing if returns false
if (false === _execCallback(null, o.onsizecontent_start)) return;
// skip re-measuring offsets if live-resizing
if (!_c.isLayoutBusy || m.top == undefined || remeasure || o.resizeContentWhileDragging) {
_measure();
// if any footers are below pane-bottom, they may not measure correctly,
// so allow pane overflow and re-measure
if (m.hiddenFooters > 0 && $P.css("overflow") == "hidden") {
$P.css("overflow", "visible");
_measure(); // remeasure while overflowing
$P.css("overflow", "hidden");
}
}
// NOTE: spaceAbove/Below *includes* the pane's paddingTop/Bottom, but not pane.borders
var newH = s.innerHeight - (m.spaceAbove - s.css.paddingTop) - (m.spaceBelow - s.css.paddingBottom);
if (!$C.is(":visible") || m.height != newH) {
// size the Content element to fit new pane-size - will autoHide if not enough room
setOuterHeight($C, newH, true); // true=autoHide
m.height = newH; // save new height
};
if (state.initialized) {
_execCallback(pane, o.onsizecontent_end || o.onsizecontent);
resizeNestedLayout(pane);
}
function _below ($E) {
return max(s.css.paddingBottom, (parseInt($E.css("marginBottom"), 10) || 0));
};
function _measure () {
var
ignore = options[pane].contentIgnoreSelector
, $Fs = $C.nextAll().not(ignore || ':lt(0)') // not :lt(0) = ALL
, $Fs_vis = $Fs.filter(':visible')
, $F = $Fs_vis.filter(':last')
;
m = {
top: $C[0].offsetTop
, height: $C.outerHeight()
, numFooters: $Fs.length
, hiddenFooters: $Fs.length - $Fs_vis.length
, spaceBelow: 0 // correct if no content footer ($E)
}
m.spaceAbove = m.top; // just for state - not used in calc
m.bottom = m.top + m.height;
if ($F.length)
//spaceBelow = (LastFooter.top + LastFooter.height) [footerBottom] - Content.bottom + max(LastFooter.marginBottom, pane.paddingBotom)
m.spaceBelow = ($F[0].offsetTop + $F.outerHeight()) - m.bottom + _below($F);
else // no footer - check marginBottom on Content element itself
m.spaceBelow = _below($C);
};
});
};
/**
* Called every time a pane is opened, closed, or resized to slide the togglers to 'center' and adjust their length if necessary
*
* @see initHandles(), open(), close(), resizeAll()
* @param {string=} panes The pane(s) being resized
*/
var sizeHandles = function (panes) {
if (!panes || panes == "all") panes = _c.borderPanes;
$.each(panes.split(","), function (i, pane) {
var
o = options[pane]
, s = state[pane]
, $P = $Ps[pane]
, $R = $Rs[pane]
, $T = $Ts[pane]
, $TC
;
if (!$P || !$R) 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) // 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(); // s.outerWidth ||
s.resizerLength = paneLen;
$R.css({
width: max(1, cssW($R, paneLen)) // account for borders & padding
, height: max(0, cssH($R, spacing)) // ditto
, left: _cssNum($P, "left")
});
}
else { // east/west
paneLen = $P.outerHeight(); // s.outerHeight ||
s.resizerLength = paneLen;
$R.css({
height: max(1, cssH($R, paneLen)) // account for borders & padding
, width: max(0, cssW($R, spacing)) // ditto
, top: sC.insetTop + getPaneSize("north", true) // TODO: what if no North pane?
//, top: _cssNum($Ps["center"], "top")
});
}
// remove hover classes
removeHover( o, $R );
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 (isStr(togAlign)) {
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, 10); //
if (togAlign >= 0) offset = x;
else offset = paneLen - togLen + x; // NOTE: x is negative!
}
}
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
, top: 0
});
// CENTER the toggler content SPAN
$T.children(".content").each(function(){
$TC = $(this);
$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
, left: 0
});
// CENTER the toggler content SPAN
$T.children(".content").each(function(){
$TC = $(this);
$TC.css("marginTop", Math.floor((height-$TC.outerHeight())/2)); // could be negative
});
}
// remove ALL hover classes
removeHover( 0, $T );
}
// DONE measuring and sizing this resizer/toggler, so can be 'hidden' now
if (!state.initialized && (o.initHidden || s.noRoom)) {
$R.hide();
if ($T) $T.hide();
}
});
};
var enableClosable = function (pane) {
var $T = $Ts[pane], o = options[pane];
if (!$T) return;
o.closable = true;
$T .bind("click."+ sID, function(evt){ evt.stopPropagation(); toggle(pane); })
.bind("mouseenter."+ sID, addHover)
.bind("mouseleave."+ sID, removeHover)
.css("visibility", "visible")
.css("cursor", "pointer")
.attr("title", state[pane].isClosed ? o.togglerTip_closed : o.togglerTip_open) // may be blank
.show()
;
};
var disableClosable = function (pane, hide) {
var $T = $Ts[pane];
if (!$T) return;
options[pane].closable = false;
// is closable is disable, then pane MUST be open!
if (state[pane].isClosed) open(pane, false, true);
$T .unbind("."+ sID)
.css("visibility", hide ? "hidden" : "visible") // instead of hide(), which creates logic issues
.css("cursor", "default")
.attr("title", "")
;
};
var enableSlidable = function (pane) {
var $R = $Rs[pane], o = options[pane];
if (!$R || !$R.data('draggable')) return;
options[pane].slidable = true;
if (s.isClosed)
bindStartSlidingEvent(pane, true);
};
var disableSlidable = function (pane) {
var $R = $Rs[pane];
if (!$R) return;
options[pane].slidable = false;
if (state[pane].isSliding)
close(pane, false, true);
else {
bindStartSlidingEvent(pane, false);
$R .css("cursor", "default")
.attr("title", "")
;
removeHover(null, $R[0]); // in case currently hovered
}
};
var enableResizable = function (pane) {
var $R = $Rs[pane], o = options[pane];
if (!$R || !$R.data('draggable')) return;
o.resizable = true;
$R .draggable("enable")
.bind("mouseenter."+ sID, onResizerEnter)
.bind("mouseleave."+ sID, onResizerLeave)
;
if (!state[pane].isClosed)
$R .css("cursor", o.resizerCursor)
.attr("title", o.resizerTip)
;
};
var disableResizable = function (pane) {
var $R = $Rs[pane];
if (!$R || !$R.data('draggable')) return;
options[pane].resizable = false;
$R .draggable("disable")
.unbind("."+ sID)
.css("cursor", "default")
.attr("title", "")
;
removeHover(null, $R[0]); // in case currently hovered
};
/**
* Move a pane from source-side (eg, west) to target-side (eg, east)
* If pane exists on target-side, move that to source-side, ie, 'swap' the panes
*
* @param {string} pane1 The pane/edge being swapped
* @param {string} pane2 ditto
*/
var swapPanes = function (pane1, pane2) {
// change state.edge NOW so callbacks can know where pane is headed...
state[pane1].edge = pane2;
state[pane2].edge = pane1;
// run these even if NOT state.initialized
var cancelled = false;
if (false === _execCallback(pane1, options[pane1].onswap_start)) cancelled = true;
if (!cancelled && false === _execCallback(pane2, options[pane2].onswap_start)) cancelled = true;
if (cancelled) {
state[pane1].edge = pane1; // reset
state[pane2].edge = pane2;
return;
}
var
oPane1 = copy( pane1 )
, oPane2 = copy( pane2 )
, sizes = {}
;
sizes[pane1] = oPane1 ? oPane1.state.size : 0;
sizes[pane2] = oPane2 ? oPane2.state.size : 0;
// clear pointers & state
$Ps[pane1] = false;
$Ps[pane2] = false;
state[pane1] = {};
state[pane2] = {};
// ALWAYS remove the resizer & toggler elements
if ($Ts[pane1]) $Ts[pane1].remove();
if ($Ts[pane2]) $Ts[pane2].remove();
if ($Rs[pane1]) $Rs[pane1].remove();
if ($Rs[pane2]) $Rs[pane2].remove();
$Rs[pane1] = $Rs[pane2] = $Ts[pane1] = $Ts[pane2] = false;
// transfer element pointers and data to NEW Layout keys
move( oPane1, pane2 );
move( oPane2, pane1 );
// cleanup objects
oPane1 = oPane2 = sizes = null;
// make panes 'visible' again
if ($Ps[pane1]) $Ps[pane1].css(_c.visible);
if ($Ps[pane2]) $Ps[pane2].css(_c.visible);
// fix any size discrepancies caused by swap
resizeAll();
// run these even if NOT state.initialized
_execCallback(pane1, options[pane1].onswap_end || options[pane1].onswap);
_execCallback(pane2, options[pane2].onswap_end || options[pane2].onswap);
return;
function copy (n) { // n = pane
var
$P = $Ps[n]
, $C = $Cs[n]
;
return !$P ? false : {
pane: n
, P: $P ? $P[0] : false
, C: $C ? $C[0] : false
, state: $.extend({}, state[n])
, options: $.extend({}, options[n])
}
};
function move (oPane, pane) {
if (!oPane) return;
var
P = oPane.P
, C = oPane.C
, oldPane = oPane.pane
, c = _c[pane]
, side = c.side.toLowerCase()
, inset = "inset"+ c.side
// save pane-options that should be retained
, s = $.extend({}, state[pane])
, o = options[pane]
// RETAIN side-specific FX Settings - more below
, fx = { resizerCursor: o.resizerCursor }
, re, size, pos
;
$.each("fxName,fxSpeed,fxSettings".split(","), function (i, k) {
fx[k] = o[k];
fx[k +"_open"] = o[k +"_open"];
fx[k +"_close"] = o[k +"_close"];
});
// update object pointers and attributes
$Ps[pane] = $(P)
.data("layoutEdge", pane)
.css(_c.hidden)
.css(c.cssReq)
;
$Cs[pane] = C ? $(C) : false;
// set options and state
options[pane] = $.extend({}, oPane.options, fx);
state[pane] = $.extend({}, oPane.state);
// change classNames on the pane, eg: ui-layout-pane-east ==> ui-layout-pane-west
re = new RegExp(o.paneClass +"-"+ oldPane, "g");
P.className = P.className.replace(re, o.paneClass +"-"+ pane);
// ALWAYS regenerate the resizer & toggler elements
initHandles(pane); // create the required resizer & toggler
// if moving to different orientation, then keep 'target' pane size
if (c.dir != _c[oldPane].dir) {
size = sizes[pane] || 0;
setSizeLimits(pane); // update pane-state
size = max(size, state[pane].minSize);
// use manualSizePane to disable autoResize - not useful after panes are swapped
manualSizePane(pane, size, true); // true = skipCallback
}
else // move the resizer here
$Rs[pane].css(side, sC[inset] + (state[pane].isVisible ? getPaneSize(pane) : 0));
// ADD CLASSNAMES & SLIDE-BINDINGS
if (oPane.state.isVisible && !s.isVisible)
setAsOpen(pane, true); // true = skipCallback
else {
setAsClosed(pane);
bindStartSlidingEvent(pane, true); // will enable events IF option is set
}
// DESTROY the object
oPane = null;
};
};
/**
* Capture keys when enableCursorHotkey - toggle pane if hotkey pressed
*
* @see 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 - $.ui.keyCode.UP
, 40: "south" // Down Cursor - $.ui.keyCode.DOWN
, 37: "west" // Left Cursor - $.ui.keyCode.LEFT
, 39: "east" // Right Cursor - $.ui.keyCode.RIGHT
}
, ALT = evt.altKey // no worky!
, SHIFT = evt.shiftKey
, CTRL = evt.ctrlKey
, CURSOR = (CTRL && code >= 37 && code <= 40)
, o, k, m, pane
;
if (CURSOR && options[PANE[code]].enableCursorHotkey) // valid cursor-hotkey
pane = PANE[code];
else if (CTRL || SHIFT) // 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
}
}
});
// validate pane
if (!pane || !$Ps[pane] || !options[pane].closable || state[pane].isHidden)
return true;
toggle(pane);
evt.stopPropagation();
evt.returnValue = false; // CANCEL key
return false;
};
/*
* ######################################
* UTILITY METHODS
* called externally or by initButtons
* ######################################
*/
/**
* Change/reset a pane's overflow setting & zIndex to allow popups/drop-downs to work
*
* @param {Object=} el (optional) Can also be 'bound' to a click, mouseOver, or other event
*/
function allowOverflow (el) {
if (this && this.tagName) el = this; // BOUND to element
var $P;
if (isStr(el))
$P = $Ps[el];
else if ($(el).data("layoutRole"))
$P = $(el);
else
$(el).parents().each(function(){
if ($(this).data("layoutRole")) {
$P = $(this);
return false; // BREAK
}
});
if (!$P || !$P.length) return; // INVALID
var
pane = $P.data("layoutEdge")
, 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 + 2) }
, 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.match(/visible|auto/)) {
curCSS.overflowX = ofX;
newCSS.overflowX = "visible";
}
if (ofY && !ofY.match(/visible|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 (el) {
if (this && this.tagName) el = this; // BOUND to element
var $P;
if (isStr(el))
$P = $Ps[el];
else if ($(el).data("layoutRole"))
$P = $(el);
else
$(el).parents().each(function(){
if ($(this).data("layoutRole")) {
$P = $(this);
return false; // BREAK
}
});
if (!$P || !$P.length) return; // INVALID
var
pane = $P.data("layoutEdge")
, 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;
};
/**
* Helper function to validate params received by addButton utilities
*
* Two classes are added to the element, based on the buttonClass...
* The type of button is appended to create the 2nd className:
* - ui-layout-button-pin
* - ui-layout-pane-button-toggle
* - ui-layout-pane-button-open
* - ui-layout-pane-button-close
*
* @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
* @param {string} pane Name of the pane the button is for: 'north', 'south', etc.
* @return {Array.<Object>} If both params valid, the element matching 'selector' in a jQuery wrapper - otherwise returns null
*/
function getBtn (selector, pane, action) {
var $E = $(selector);
if (!$E.length) // element not found
alert(lang.errButton + lang.selector +": "+ selector);
else if (_c.borderPanes.indexOf(pane) == -1) // invalid 'pane' sepecified
alert(lang.errButton + lang.Pane.toLowerCase() +": "+ pane);
else { // VALID
var btn = options[pane].buttonClass +"-"+ action;
$E
.addClass( btn +" "+ btn +"-"+ pane )
.data("layoutName", options.name) // add layout identifier - even if blank!
;
return $E;
}
return null; // INVALID
};
/**
* NEW syntax for binding layout-buttons - will eventually replace addToggleBtn, addOpenBtn, etc.
*
* @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
* @param {string} action
* @param {string} pane
*/
function bindButton (selector, action, pane) {
switch (action.toLowerCase()) {
case "toggle": addToggleBtn(selector, pane); break;
case "open": addOpenBtn(selector, pane); break;
case "close": addCloseBtn(selector, pane); break;
case "pin": addPinBtn(selector, pane); break;
case "toggle-slide": addToggleBtn(selector, pane, true); break;
case "open-slide": addOpenBtn(selector, pane, true); break;
}
};
/**
* Add a custom Toggler button for a pane
*
* @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
* @param {string} pane Name of the pane the button is for: 'north', 'south', etc.
* @param {boolean=} slide true = slide-open, false = pin-open
*/
function addToggleBtn (selector, pane, slide) {
var $E = getBtn(selector, pane, "toggle");
if ($E)
$E.click(function (evt) {
toggle(pane, !!slide);
evt.stopPropagation();
});
};
/**
* Add a custom Open button for a pane
*
* @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
* @param {string} pane Name of the pane the button is for: 'north', 'south', etc.
* @param {boolean=} slide true = slide-open, false = pin-open
*/
function addOpenBtn (selector, pane, slide) {
var $E = getBtn(selector, pane, "open");
if ($E)
$E
.attr("title", lang.Open)
.click(function (evt) {
open(pane, !!slide);
evt.stopPropagation();
})
;
};
/**
* Add a custom Close button for a pane
*
* @param {(string|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-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", lang.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|!Object)} selector jQuery selector (or element) for button, eg: ".ui-layout-north .toggle-button"
* @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
}
};
/**
* 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
*
* @see open(), close()
* @param {string} pane These are the params returned to callbacks by layout()
* @param {boolean} 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);
});
};
/**
* Change the class of the pin button to make it look 'up' or 'down'
*
* @see addPinBtn(), syncPinBtns()
* @param {Array.<Object>} $Pin The pin-span element in a jQuery wrapper
* @param {string} pane These are the params returned to callbacks by layout()
* @param {boolean} doPin true = set the pin 'down', false = set it 'up'
*/
function setPinState ($Pin, pane, doPin) {
var updown = $Pin.attr("pin");
if (updown && doPin == (updown=="down")) return; // already in correct state
var
pin = options[pane].buttonClass +"-pin"
, side = pin +"-"+ pane
, UP = pin +"-up "+ side +"-up"
, DN = pin +"-down "+side +"-down"
;
$Pin
.attr("pin", doPin ? "down" : "up") // logic
.attr("title", doPin ? lang.Unpin : lang.Pin)
.removeClass( doPin ? UP : DN )
.addClass( doPin ? DN : UP )
;
};
/*
* LAYOUT STATE MANAGEMENT
*
* @example .layout({ cookie: { name: "myLayout", keys: "west.isClosed,east.isClosed" } })
* @example .layout({ cookie__name: "myLayout", cookie__keys: "west.isClosed,east.isClosed" })
* @example myLayout.getState( "west.isClosed,north.size,south.isHidden" );
* @example myLayout.saveCookie( "west.isClosed,north.size,south.isHidden", {expires: 7} );
* @example myLayout.deleteCookie();
* @example myLayout.loadCookie();
* @example var hSaved = myLayout.state.cookie;
*/
function isCookiesEnabled () {
// TODO: is the cookieEnabled property common enough to be useful???
return (navigator.cookieEnabled != 0);
};
/**
* Read & return data from the cookie - as JSON
*
* @param {Object=} opts
*/
function getCookie (opts) {
var
o = $.extend( {}, options.cookie, opts || {} )
, name = o.name || options.name || "Layout"
, c = document.cookie
, cs = c ? c.split(';') : []
, pair // loop var
;
for (var i=0, n=cs.length; i < n; i++) {
pair = $.trim(cs[i]).split('='); // name=value pair
if (pair[0] == name) // found the layout cookie
// convert cookie string back to a hash
return decodeJSON( decodeURIComponent(pair[1]) );
}
return "";
};
/**
* Get the current layout state and save it to a cookie
*
* @param {(string|Array)=} keys
* @param {Object=} opts
*/
function saveCookie (keys, opts) {
var
o = $.extend( {}, options.cookie, opts || {} )
, name = o.name || options.name || "Layout"
, params = ''
, date = ''
, clear = false
;
if (o.expires.toUTCString)
date = o.expires;
else if (typeof o.expires == 'number') {
date = new Date();
if (o.expires > 0)
date.setDate(date.getDate() + o.expires);
else {
date.setYear(1970);
clear = true;
}
}
if (date) params += ';expires='+ date.toUTCString();
if (o.path) params += ';path='+ o.path;
if (o.domain) params += ';domain='+ o.domain;
if (o.secure) params += ';secure';
if (clear) {
state.cookie = {}; // clear data
document.cookie = name +'='+ params; // expire the cookie
}
else {
state.cookie = getState(keys || o.keys); // read current panes-state
document.cookie = name +'='+ encodeURIComponent( encodeJSON(state.cookie) ) + params; // write cookie
}
return $.extend({}, state.cookie); // return COPY of state.cookie
};
/**
* Remove the state cookie
*/
function deleteCookie () {
saveCookie('', { expires: -1 });
};
/**
* Get data from the cookie and USE IT to loadState
*
* @param {Object=} opts
*/
function loadCookie (opts) {
var o = getCookie(opts); // READ the cookie
if (o) {
state.cookie = $.extend({}, o); // SET state.cookie
loadState(o); // LOAD the retrieved state
}
return o;
};
/**
* Update layout options from the cookie, if one exists
*
* @param {Object=} opts
* @param {boolean=} animate
*/
function loadState (opts, animate) {
$.extend( true, options, opts ); // update layout options
// if layout has already been initialized, then UPDATE layout state
if (state.initialized) {
var pane, o, v, a = !animate;
$.each(_c.allPanes.split(","), function (idx, pane) {
o = opts[ pane ];
if (typeof o != 'object') return; // no key, continue
v = o.initHidden;
if (v === true) hide(pane, a);
if (v === false) show(pane, false, a);
v = o.size;
if (v > 0) sizePane(pane, v);
v = o.initClosed;
if (v === true) close(pane, false, a);
if (v === false) open(pane, false, a );
});
}
};
/**
* Get the *current layout state* and return it as a hash
*
* @param {(string|Array)=} keys
*/
function getState (keys) {
var
data = {}
, alt = { isClosed: 'initClosed', isHidden: 'initHidden' }
, pair, pane, key, val
;
if (!keys) keys = options.cookie.keys; // if called by user
if ($.isArray(keys)) keys = keys.join(",");
// convert keys to an array and change delimiters from '__' to '.'
keys = keys.replace(/__/g, ".").split(',');
// loop keys and create a data hash
for (var i=0,n=keys.length; i < n; i++) {
pair = keys[i].split(".");
pane = pair[0];
key = pair[1];
if (_c.allPanes.indexOf(pane) < 0) continue; // bad pane!
val = state[ pane ][ key ];
if (val == undefined) continue;
if (key=="isClosed" && state[pane]["isSliding"])
val = true; // if sliding, then *really* isClosed
( data[pane] || (data[pane]={}) )[ alt[key] ? alt[key] : key ] = val;
}
return data;
};
/**
* Stringify a JSON hash so can save in a cookie or db-field
*/
function encodeJSON (JSON) {
return parse( JSON );
function parse (h) {
var D=[], i=0, k, v, t; // k = key, v = value
for (k in h) {
v = h[k];
t = typeof v;
if (t == 'string') // STRING - add quotes
v = '"'+ v +'"';
else if (t == 'object') // SUB-KEY - recurse into it
v = parse(v);
D[i++] = '"'+ k +'":'+ v;
}
return "{"+ D.join(",") +"}";
};
};
/**
* Convert stringified JSON back to a hash object
*/
function decodeJSON (str) {
try { return window["eval"]("("+ str +")") || {}; }
catch (e) { return {}; }
};
/*
* #####################
* CREATE/RETURN LAYOUT
* #####################
*/
// validate that container exists
var $Container = $(this).eq(0); // FIRST matching Container element
if (!$Container.length) {
//alert( lang.errContainerMissing );
return null;
};
// Users retreive Instance of a layout with: $Container.layout() OR $Container.data("layout")
// return the Instance-pointer if layout has already been initialized
if ($Container.data("layoutContainer") && $Container.data("layout"))
return $Container.data("layout"); // cached pointer
// init global vars
var
$Ps = {} // Panes x5 - set in initPanes()
, $Cs = {} // Content x5 - set in initPanes()
, $Rs = {} // Resizers x4 - set in initHandles()
, $Ts = {} // Togglers x4 - set in initHandles()
// aliases for code brevity
, sC = state.container // alias for easy access to 'container dimensions'
, sID = state.id // alias for unique layout ID/namespace - eg: "layout435"
;
// create Instance object to expose data & option Properties, and primary action Methods
var Instance = {
options: options // property - options hash
, state: state // property - dimensions hash
, container: $Container // property - object pointers for layout container
, panes: $Ps // property - object pointers for ALL Panes: panes.north, panes.center
, contents: $Cs // property - object pointers for ALL Content: content.north, content.center
, resizers: $Rs // property - object pointers for ALL Resizers, eg: resizers.north
, togglers: $Ts // property - object pointers for ALL Togglers, eg: togglers.north
, toggle: toggle // method - pass a 'pane' ("north", "west", etc)
, hide: hide // method - ditto
, show: show // method - ditto
, open: open // method - ditto
, close: close // method - ditto
, slideOpen: slideOpen // method - ditto
, slideClose: slideClose // method - ditto
, slideToggle: slideToggle // method - ditto
, initContent: initContent // method - ditto
, sizeContent: sizeContent // method - pass a 'pane'
, sizePane: manualSizePane // method - pass a 'pane' AND an 'outer-size' in pixels or percent, or 'auto'
, swapPanes: swapPanes // method - pass TWO 'panes' - will swap them
, resizeAll: resizeAll // method - no parameters
, destroy: destroy // method - no parameters
, addPane: addPane // method - pass a 'pane'
, removePane: removePane // method - pass a 'pane' to remove from layout, add 'true' to delete the pane-elem
, setSizeLimits: setSizeLimits // method - pass a 'pane' - update state min/max data
, bindButton: bindButton // utility - pass element selector, 'action' and 'pane' (E, "toggle", "west")
, addToggleBtn: addToggleBtn // utility - pass element selector and 'pane' (E, "west")
, addOpenBtn: addOpenBtn // utility - ditto
, addCloseBtn: addCloseBtn // utility - ditto
, addPinBtn: addPinBtn // utility - ditto
, allowOverflow: allowOverflow // utility - pass calling element (this)
, resetOverflow: resetOverflow // utility - ditto
, encodeJSON: encodeJSON // method - pass a JSON object
, decodeJSON: decodeJSON // method - pass a string of encoded JSON
, getState: getState // method - returns hash of current layout-state
, getCookie: getCookie // method - update options from cookie - returns hash of cookie data
, saveCookie: saveCookie // method - optionally pass keys-list and cookie-options (hash)
, deleteCookie: deleteCookie // method
, loadCookie: loadCookie // method - update options from cookie - returns hash of cookie data
, loadState: loadState // method - pass a hash of state to use to update options
, cssWidth: cssW // utility - pass element and target outerWidth
, cssHeight: cssH // utility - ditto
, enableClosable: enableClosable
, disableClosable: disableClosable
, enableSlidable: enableSlidable
, disableSlidable: disableSlidable
, enableResizable: enableResizable
, disableResizable: disableResizable
};
// create the border layout NOW
_create();
// return the Instance object
return Instance;
}
})( jQuery );

View File

@ -1,142 +0,0 @@
/*
jquery.layout 1.3.0 - Release Candidate 29.14
$Date: 2011-02-13 08:00:00 (Sun, 13 Feb 2011) $
$Rev: 302914 $
Copyright (c) 2010
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.
Changelog: http://layout.jquery-dev.net/changelog.cfm#1.3.0.rc29.13
Docs: http://layout.jquery-dev.net/documentation.html
Tips: http://layout.jquery-dev.net/tips.html
Help: http://groups.google.com/group/jquery-ui-layout
*/
(function($){var $b=$.browser;$.layout={browser:{mozilla:!!$b.mozilla,webkit:!!$b.webkit||!!$b.safari,msie:!!$b.msie,isIE6:!!$b.msie&&$b.version==6,boxModel:false},scrollbarWidth:function(){return window.scrollbarWidth||$.layout.getScrollbarSize("width")},scrollbarHeight:function(){return window.scrollbarHeight||$.layout.getScrollbarSize("height")},getScrollbarSize:function(dim){var $c=$('<div style="position: absolute; top: -10000px; left: -10000px; width: 100px; height: 100px; overflow: scroll;"></div>').appendTo("body");
var d={width:$c.width()-$c[0].clientWidth,height:$c.height()-$c[0].clientHeight};$c.remove();window.scrollbarWidth=d.width;window.scrollbarHeight=d.height;return dim.match(/^(width|height)$/i)?d[dim]:d},showInvisibly:function($E,force){if(!$E)return{};if(!$E.jquery)$E=$($E);var CSS={display:$E.css("display"),visibility:$E.css("visibility")};if(force||CSS.display=="none"){$E.css({display:"block",visibility:"hidden"});return CSS}else return{}},getElemDims:function($E){var d={},x=d.css={},i={},b,p,off=
$E.offset();d.offsetLeft=off.left;d.offsetTop=off.top;$.each("Left,Right,Top,Bottom".split(","),function(idx,e){b=x["border"+e]=$.layout.borderWidth($E,e);p=x["padding"+e]=$.layout.cssNum($E,"padding"+e);i[e]=b+p;d["inset"+e]=p});d.offsetWidth=$E.innerWidth();d.offsetHeight=$E.innerHeight();d.outerWidth=$E.outerWidth();d.outerHeight=$E.outerHeight();d.innerWidth=d.outerWidth-i.Left-i.Right;d.innerHeight=d.outerHeight-i.Top-i.Bottom;x.width=$E.width();x.height=$E.height();return d},getElemCSS:function($E,
list){var CSS={},style=$E[0].style,props=list.split(","),sides="Top,Bottom,Left,Right".split(","),attrs="Color,Style,Width".split(","),p,s,a,i,j,k;for(i=0;i<props.length;i++){p=props[i];if(p.match(/(border|padding|margin)$/))for(j=0;j<4;j++){s=sides[j];if(p=="border")for(k=0;k<3;k++){a=attrs[k];CSS[p+s+a]=style[p+s+a]}else CSS[p+s]=style[p+s]}else CSS[p]=style[p]}return CSS},cssWidth:function($E,outerWidth){var b=$.layout.borderWidth,n=$.layout.cssNum;if(outerWidth<=0)return 0;if(!$.layout.browser.boxModel)return outerWidth;
var W=outerWidth-b($E,"Left")-b($E,"Right")-n($E,"paddingLeft")-n($E,"paddingRight");return Math.max(0,W)},cssHeight:function($E,outerHeight){var b=$.layout.borderWidth,n=$.layout.cssNum;if(outerHeight<=0)return 0;if(!$.layout.browser.boxModel)return outerHeight;var H=outerHeight-b($E,"Top")-b($E,"Bottom")-n($E,"paddingTop")-n($E,"paddingBottom");return Math.max(0,H)},cssNum:function($E,prop){if(!$E.jquery)$E=$($E);var CSS=$.layout.showInvisibly($E);var val=parseInt($.curCSS($E[0],prop,true),10)||
0;$E.css(CSS);return val},borderWidth:function(el,side){if(el.jquery)el=el[0];var b="border"+side.substr(0,1).toUpperCase()+side.substr(1);return $.curCSS(el,b+"Style",true)=="none"?0:parseInt($.curCSS(el,b+"Width",true),10)||0},isMouseOverElem:function(evt,el){var $E=$(el||this),d=$E.offset(),T=d.top,L=d.left,R=L+$E.outerWidth(),B=T+$E.outerHeight(),x=evt.pageX,y=evt.pageY;return $.layout.browser.msie&&x<0&&y<0||x>=L&&x<=R&&y>=T&&y<=B}};$.fn.layout=function(opts){var lang={Pane:"Pane",Open:"Open",
Close:"Close",Resize:"Resize",Slide:"Slide Open",Pin:"Pin",Unpin:"Un-Pin",selector:"selector",msgNoRoom:"Not enough room to show this pane.",errContainerMissing:"UI Layout Initialization Error\n\nThe specified layout-container does not exist.",errCenterPaneMissing:"UI Layout Initialization Error\n\nThe center-pane element does not exist.\n\nThe center-pane is a required element.",errContainerHeight:"UI Layout Initialization Warning\n\nThe layout-container \"CONTAINER\" has no height.\n\nTherefore the layout is 0-height and hence 'invisible'!",
errButton:"Error Adding Button \n\nInvalid "};var options={name:"",containerClass:"ui-layout-container",scrollToBookmarkOnLoad:true,resizeWithWindow:true,resizeWithWindowDelay:200,resizeWithWindowMaxDelay:0,onresizeall_start:null,onresizeall_end:null,onload_start:null,onload_end:null,onunload_start:null,onunload_end:null,autoBindCustomButtons:false,zIndex:null,defaults:{applyDemoStyles:false,closable:true,resizable:true,slidable:true,initClosed:false,initHidden:false,contentSelector:".ui-layout-content",
contentIgnoreSelector:".ui-layout-ignore",findNestedContent:false,paneClass:"ui-layout-pane",resizerClass:"ui-layout-resizer",togglerClass:"ui-layout-toggler",buttonClass:"ui-layout-button",minSize:0,maxSize:0,spacing_open:6,spacing_closed:6,togglerLength_open:50,togglerLength_closed:50,togglerAlign_open:"center",togglerAlign_closed:"center",togglerTip_open:lang.Close,togglerTip_closed:lang.Open,togglerContent_open:"",togglerContent_closed:"",resizerDblClickToggle:true,autoResize:true,autoReopen:true,
resizerDragOpacity:1,maskIframesOnResize:true,resizeNestedLayout:true,resizeWhileDragging:false,resizeContentWhileDragging:false,noRoomToOpenTip:lang.msgNoRoom,resizerTip:lang.Resize,sliderTip:lang.Slide,sliderCursor:"pointer",slideTrigger_open:"click",slideTrigger_close:"mouseleave",slideDelay_open:300,slideDelay_close:300,hideTogglerOnSlide:false,preventQuickSlideClose:!!($.browser.webkit||$.browser.safari),preventPrematureSlideClose:false,showOverflowOnHover:false,enableCursorHotkey:true,customHotkeyModifier:"SHIFT",
fxName:"slide",fxSpeed:null,fxSettings:{},fxOpacityFix:true,triggerEventsOnLoad:false,triggerEventsWhileDragging:true,onshow_start:null,onshow_end:null,onhide_start:null,onhide_end:null,onopen_start:null,onopen_end:null,onclose_start:null,onclose_end:null,onresize_start:null,onresize_end:null,onsizecontent_start:null,onsizecontent_end:null,onswap_start:null,onswap_end:null,ondrag_start:null,ondrag_end:null},north:{paneSelector:".ui-layout-north",size:"auto",resizerCursor:"n-resize",customHotkey:""},
south:{paneSelector:".ui-layout-south",size:"auto",resizerCursor:"s-resize",customHotkey:""},east:{paneSelector:".ui-layout-east",size:200,resizerCursor:"e-resize",customHotkey:""},west:{paneSelector:".ui-layout-west",size:200,resizerCursor:"w-resize",customHotkey:""},center:{paneSelector:".ui-layout-center",minWidth:0,minHeight:0},useStateCookie:false,cookie:{name:"",autoSave:true,autoLoad:true,domain:"",path:"",expires:"",secure:false,keys:"north.size,south.size,east.size,west.size,"+"north.isClosed,south.isClosed,east.isClosed,west.isClosed,"+
"north.isHidden,south.isHidden,east.isHidden,west.isHidden"}};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 state={id:"layout"+(new Date).getTime(),initialized:false,container:{},north:{},south:{},east:{},west:{},center:{},cookie:{}};var _c={allPanes:"north,south,west,east,center",
borderPanes:"north,south,west,east",altSide:{north:"south",south:"north",east:"west",west:"east"},hidden:{visibility:"hidden"},visible:{visibility:"visible"},zIndex:{pane_normal:1,resizer_normal:2,iframe_mask:2,pane_sliding:100,pane_animate:1E3,resizer_drag:1E4},resizers:{cssReq:{position:"absolute",padding:0,margin:0,fontSize:"1px",textAlign:"left",overflow:"hidden"},cssDemo:{background:"#DDD",border:"none"}},togglers:{cssReq:{position:"absolute",display:"block",padding:0,margin:0,overflow:"hidden",
textAlign:"center",fontSize:"1px",cursor:"pointer",zIndex:1},cssDemo:{background:"#AAA"}},content:{cssReq:{position:"relative"},cssDemo:{overflow:"auto",padding:"10px"},cssDemoPane:{overflow:"hidden",padding:0}},panes:{cssReq:{position:"absolute",margin:0},cssDemo:{padding:"10px",background:"#FFF",border:"1px solid #BBB",overflow:"auto"}},north:{side:"Top",sizeType:"Height",dir:"horz",cssReq:{top:0,bottom:"auto",left:0,right:0,width:"auto"},pins:[]},south:{side:"Bottom",sizeType:"Height",dir:"horz",
cssReq:{top:"auto",bottom:0,left:0,right:0,width:"auto"},pins:[]},east:{side:"Right",sizeType:"Width",dir:"vert",cssReq:{left:"auto",right:0,top:"auto",bottom:"auto",height:"auto"},pins:[]},west:{side:"Left",sizeType:"Width",dir:"vert",cssReq:{left:0,right:"auto",top:"auto",bottom:"auto",height:"auto"},pins:[]},center:{dir:"center",cssReq:{left:"auto",right:"auto",top:"auto",bottom:"auto",height:"auto",width:"auto"}}};var timer={data:{},set:function(s,fn,ms){timer.clear(s);timer.data[s]=setTimeout(fn,
ms)},clear:function(s){var t=timer.data;if(t[s]){clearTimeout(t[s]);delete t[s]}}};var isStr=function(o){try{return typeof o=="string"||typeof o=="object"&&o.constructor.toString().match(/string/i)!==null}catch(e){return false}};var str=function(o){return isStr(o)?$.trim(o):o==undefined||o==null?"":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 a,json={cookie:{},defaults:{fxSettings:{}},north:{fxSettings:{}},south:{fxSettings:{}},
east:{fxSettings:{}},west:{fxSettings:{}},center:{fxSettings:{}}};d=d||{};if(d.effects||d.cookie||d.defaults||d.north||d.south||d.west||d.east||d.center)json=$.extend(true,json,d);else $.each(d,function(key,val){a=key.split("__");if(!a[1]||json[a[0]])json[a[1]?a[0]:"defaults"][a[1]?a[1]:a[0]]=val});return json};var _queue=function(action,pane,param){var tried=[];$.each(_c.borderPanes.split(","),function(i,p){if(_c[p].isMoving){bindCallback(p);return false}});function bindCallback(p){var c=_c[p];if(!c.doCallback){c.doCallback=
true;c.callback=action+","+pane+","+(param?1:0)}else{tried.push(p);var cbPane=c.callback.split(",")[1];if(cbPane!=pane&&!$.inArray(cbPane,tried)>=0)bindCallback(cbPane)}}};var _dequeue=function(pane){var c=_c[pane];_c.isLayoutBusy=false;delete c.isMoving;if(!c.doCallback||!c.callback)return;c.doCallback=false;var cb=c.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(!c.doCallback)c.callback=null};var _execCallback=function(pane,
v_fn){if(!v_fn)return;var fn;try{if(typeof v_fn=="function")fn=v_fn;else if(!isStr(v_fn))return;else if(v_fn.match(/,/)){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")if(pane&&$Ps[pane])return fn(pane,$Ps[pane],$.extend({},state[pane]),options[pane],options.name);else return fn(Instance,$.extend({},state),options,options.name)}catch(ex){}};var _showInvisibly=function($E,force){if(!$E)return{};if(!$E.jquery)$E=
$($E);var CSS={display:$E.css("display"),visibility:$E.css("visibility")};if(force||CSS.display=="none"){$E.css({display:"block",visibility:"hidden"});return CSS}else return{}};var _fixIframe=function(pane){if(state.browser.mozilla)return;var $P=$Ps[pane];if(state[pane].tagName=="IFRAME")$P.css(_c.hidden).css(_c.visible);else $P.find("IFRAME").css(_c.hidden).css(_c.visible)};var _cssNum=function($E,prop){if(!$E.jquery)$E=$($E);var CSS=_showInvisibly($E);var val=parseInt($.curCSS($E[0],prop,true),
10)||0;$E.css(CSS);return val};var _borderWidth=function(E,side){if(E.jquery)E=E[0];var b="border"+side.substr(0,1).toUpperCase()+side.substr(1);return $.curCSS(E,b+"Style",true)=="none"?0:parseInt($.curCSS(E,b+"Width",true),10)||0};var cssW=function(el,outerWidth){var str=isStr(el),$E=str?$Ps[el]:$(el);if(isNaN(outerWidth))outerWidth=str?getPaneSize(el):$E.outerWidth();if(outerWidth<=0)return 0;if(!state.browser.boxModel)return outerWidth;var W=outerWidth-_borderWidth($E,"Left")-_borderWidth($E,
"Right")-_cssNum($E,"paddingLeft")-_cssNum($E,"paddingRight");return max(0,W)};var cssH=function(el,outerHeight){var str=isStr(el),$E=str?$Ps[el]:$(el);if(isNaN(outerHeight))outerHeight=str?getPaneSize(el):$E.outerHeight();if(outerHeight<=0)return 0;if(!state.browser.boxModel)return outerHeight;var H=outerHeight-_borderWidth($E,"Top")-_borderWidth($E,"Bottom")-_cssNum($E,"paddingTop")-_cssNum($E,"paddingBottom");return max(0,H)};var cssSize=function(pane,outerSize){if(_c[pane].dir=="horz")return cssH(pane,
outerSize);else return cssW(pane,outerSize)};var cssMinDims=function(pane){var dir=_c[pane].dir,d={minWidth:1001-cssW(pane,1E3),minHeight:1001-cssH(pane,1E3)};if(dir=="horz")d.minSize=d.minHeight;if(dir=="vert")d.minSize=d.minWidth;return d};var setOuterWidth=function(el,outerWidth,autoHide){var $E=el,w;if(isStr(el))$E=$Ps[el];else if(!el.jquery)$E=$(el);w=cssW($E,outerWidth);$E.css({width:w});if(w>0){if(autoHide&&$E.data("autoHidden")&&$E.innerHeight()>0){$E.show().data("autoHidden",false);if(!state.browser.mozilla)$E.css(_c.hidden).css(_c.visible)}}else if(autoHide&&
!$E.data("autoHidden"))$E.hide().data("autoHidden",true)};var setOuterHeight=function(el,outerHeight,autoHide){var $E=el,h;if(isStr(el))$E=$Ps[el];else if(!el.jquery)$E=$(el);h=cssH($E,outerHeight);$E.css({height:h,visibility:"visible"});if(h>0&&$E.innerWidth()>0){if(autoHide&&$E.data("autoHidden")){$E.show().data("autoHidden",false);if(!state.browser.mozilla)$E.css(_c.hidden).css(_c.visible)}}else if(autoHide&&!$E.data("autoHidden"))$E.hide().data("autoHidden",true)};var setOuterSize=function(el,
outerSize,autoHide){if(_c[pane].dir=="horz")setOuterHeight(el,outerSize,autoHide);else setOuterWidth(el,outerSize,autoHide)};var _parseSize=function(pane,size,dir){if(!dir)dir=_c[pane].dir;if(isStr(size)&&size.match(/%/))size=parseInt(size,10)/100;if(size===0)return 0;else if(size>=1)return parseInt(size,10);else if(size>0){var o=options,avail;if(dir=="horz")avail=sC.innerHeight-($Ps.north?o.north.spacing_open:0)-($Ps.south?o.south.spacing_open:0);else if(dir=="vert")avail=sC.innerWidth-($Ps.west?
o.west.spacing_open:0)-($Ps.east?o.east.spacing_open:0);return Math.floor(avail*size)}else if(pane=="center")return 0;else{var $P=$Ps[pane],dim=dir=="horz"?"height":"width",vis=_showInvisibly($P),s=$P.css(dim);$P.css(dim,"auto");size=dim=="height"?$P.outerHeight():$P.outerWidth();$P.css(dim,s).css(vis);return size}};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 setSizeLimits=function(pane,slide){var o=options[pane],s=state[pane],c=_c[pane],dir=c.dir,side=c.side.toLowerCase(),type=c.sizeType.toLowerCase(),isSliding=slide!=undefined?slide:s.isSliding,$P=$Ps[pane],paneSpacing=o.spacing_open,altPane=_c.altSide[pane],altS=state[altPane],$altP=$Ps[altPane],altPaneSize=!$altP||altS.isVisible===false||altS.isSliding?0:dir=="horz"?$altP.outerHeight():
$altP.outerWidth(),altPaneSpacing=(!$altP||altS.isHidden?0:options[altPane][altS.isClosed!==false?"spacing_closed":"spacing_open"])||0,containerSize=dir=="horz"?sC.innerHeight:sC.innerWidth,minCenterDims=cssMinDims("center"),minCenterSize=dir=="horz"?max(options.center.minHeight,minCenterDims.minHeight):max(options.center.minWidth,minCenterDims.minWidth),limitSize=containerSize-paneSpacing-(isSliding?0:_parseSize("center",minCenterSize,dir)+altPaneSize+altPaneSpacing),minSize=s.minSize=max(_parseSize(pane,
o.minSize),cssMinDims(pane).minSize),maxSize=s.maxSize=min(o.maxSize?_parseSize(pane,o.maxSize):1E5,limitSize),r=s.resizerPosition={},top=sC.insetTop,left=sC.insetLeft,W=sC.innerWidth,H=sC.innerHeight,rW=o.spacing_open;switch(pane){case "north":r.min=top+minSize;r.max=top+maxSize;break;case "west":r.min=left+minSize;r.max=left+maxSize;break;case "south":r.min=top+H-maxSize-rW;r.max=top+H-minSize-rW;break;case "east":r.min=left+W-maxSize-rW;r.max=left+W-minSize-rW;break}};var calcNewCenterPaneDims=
function(){var d={top:getPaneSize("north",true),bottom:getPaneSize("south",true),left:getPaneSize("west",true),right:getPaneSize("east",true),width:0,height:0};d.width=sC.innerWidth-d.left-d.right;d.height=sC.innerHeight-d.bottom-d.top;d.top+=sC.insetTop;d.bottom+=sC.insetBottom;d.left+=sC.insetLeft;d.right+=sC.insetRight;return d};var getElemDims=function($E){var d={},x=d.css={},i={},b,p,off=$E.offset();d.offsetLeft=off.left;d.offsetTop=off.top;$.each("Left,Right,Top,Bottom".split(","),function(idx,
e){b=x["border"+e]=_borderWidth($E,e);p=x["padding"+e]=_cssNum($E,"padding"+e);i[e]=b+p;d["inset"+e]=p});d.offsetWidth=$E.innerWidth();d.offsetHeight=$E.innerHeight();d.outerWidth=$E.outerWidth();d.outerHeight=$E.outerHeight();d.innerWidth=d.outerWidth-i.Left-i.Right;d.innerHeight=d.outerHeight-i.Top-i.Bottom;x.width=$E.width();x.height=$E.height();return d};var getElemCSS=function($E,list){var CSS={},style=$E[0].style,props=list.split(","),sides="Top,Bottom,Left,Right".split(","),attrs="Color,Style,Width".split(","),
p,s,a,i,j,k;for(i=0;i<props.length;i++){p=props[i];if(p.match(/(border|padding|margin)$/))for(j=0;j<4;j++){s=sides[j];if(p=="border")for(k=0;k<3;k++){a=attrs[k];CSS[p+s+a]=style[p+s+a]}else CSS[p+s]=style[p+s]}else CSS[p]=style[p]}return CSS};var getHoverClasses=function(el,allStates){var $El=$(el),type=$El.data("layoutRole"),pane=$El.data("layoutEdge"),o=options[pane],root=o[type+"Class"],_pane="-"+pane,_open="-open",_closed="-closed",_slide="-sliding",_hover="-hover ",_state=$El.hasClass(root+_closed)?
_closed:_open,_alt=_state==_closed?_open:_closed,classes=root+_hover+(root+_pane+_hover)+(root+_state+_hover)+(root+_pane+_state+_hover);if(allStates)classes+=root+_alt+_hover+(root+_pane+_alt+_hover);if(type=="resizer"&&$El.hasClass(root+_slide))classes+=root+_slide+_hover+(root+_pane+_slide+_hover);return $.trim(classes)};var addHover=function(evt,el){var $E=$(el||this);if(evt&&$E.data("layoutRole")=="toggler")evt.stopPropagation();$E.addClass(getHoverClasses($E))};var removeHover=function(evt,
el){var $E=$(el||this);$E.removeClass(getHoverClasses($E,true))};var onResizerEnter=function(evt){$("body").disableSelection();addHover(evt,this)};var onResizerLeave=function(evt,el){var e=el||this,pane=$(e).data("layoutEdge"),name=pane+"ResizerLeave";timer.clear(pane+"_openSlider");timer.clear(name);if(!el){removeHover(evt,this);timer.set(name,function(){onResizerLeave(evt,e)},200)}else if(!state[pane].isResizing)$("body").enableSelection()};var _create=function(){initOptions();var o=options;if(false===
_execCallback(null,o.onload_start))return false;if(!getPane("center").length){alert(lang.errCenterPaneMissing);return null}if(o.useStateCookie&&o.cookie.autoLoad)loadCookie();state.browser={mozilla:$.browser.mozilla,webkit:$.browser.webkit||$.browser.safari,msie:$.browser.msie,isIE6:$.browser.msie&&$.browser.version==6,boxModel:$.support.boxModel};initContainer();initPanes();sizeContent();if(o.scrollToBookmarkOnLoad){var l=self.location;if(l.hash)l.replace(l.hash)}initHotkeys();if(o.autoBindCustomButtons)initButtons();
if(o.resizeWithWindow&&!$Container.data("layoutRole"))$(window).bind("resize."+sID,windowResize);$(window).bind("unload."+sID,unload);state.initialized=true;_execCallback(null,o.onload_end||o.onload)};var windowResize=function(){var delay=Number(options.resizeWithWindowDelay)||100;if(delay>0){timer.clear("winResize");timer.set("winResize",function(){timer.clear("winResize");timer.clear("winResizeRepeater");resizeAll()},delay);if(!timer.data["winResizeRepeater"])setWindowResizeRepeater()}};var setWindowResizeRepeater=
function(){var delay=Number(options.resizeWithWindowMaxDelay);if(delay>0)timer.set("winResizeRepeater",function(){setWindowResizeRepeater();resizeAll()},delay)};var unload=function(){var o=options;state.cookie=getState();_execCallback(null,o.onunload_start);if(o.useStateCookie&&o.cookie.autoSave)saveCookie();_execCallback(null,o.onunload_end||o.onunload)};var initContainer=function(){var $C=$Container,tag=sC.tagName=$C.attr("tagName"),fullPage=tag=="BODY",props="position,margin,padding,border",CSS=
{};sC.selector=$C.selector.split(".slice")[0];sC.ref=tag+"/"+sC.selector;$C.data("layout",Instance).data("layoutContainer",sID).addClass(options.containerClass);if(!$C.data("layoutCSS")){if(fullPage){CSS=$.extend(getElemCSS($C,props),{height:$C.css("height"),overflow:$C.css("overflow"),overflowX:$C.css("overflowX"),overflowY:$C.css("overflowY")});var $H=$("html");$H.data("layoutCSS",{height:"auto",overflow:$H.css("overflow"),overflowX:$H.css("overflowX"),overflowY:$H.css("overflowY")})}else CSS=getElemCSS($C,
props+",top,bottom,left,right,width,height,overflow,overflowX,overflowY");$C.data("layoutCSS",CSS)}try{if(fullPage){$("html").css({height:"100%",overflow:"hidden",overflowX:"hidden",overflowY:"hidden"});$("body").css({position:"relative",height:"100%",overflow:"hidden",overflowX:"hidden",overflowY:"hidden",margin:0,padding:0,border:"none"})}else{CSS={overflow:"hidden"};var p=$C.css("position"),h=$C.css("height");if(!$C.data("layoutRole"))if(!p||!p.match(/fixed|absolute|relative/))CSS.position="relative";
$C.css(CSS);if($C.is(":visible")&&$C.innerHeight()<2)alert(lang.errContainerHeight.replace(/CONTAINER/,sC.ref))}}catch(ex){}$.extend(state.container,getElemDims($C))};var initHotkeys=function(panes){if(!panes||panes=="all")panes=_c.borderPanes;$.each(panes.split(","),function(i,pane){var o=options[pane];if(o.enableCursorHotkey||o.customHotkey){$(document).bind("keydown."+sID,keyDown);return false}})};var initOptions=function(){opts=_transformData(opts);var newOpts={applyDefaultStyles:"applyDemoStyles"};
renameOpts(opts.defaults);$.each(_c.allPanes.split(","),function(i,pane){renameOpts(opts[pane])});if(opts.effects){$.extend(effects,opts.effects);delete opts.effects}$.extend(options.cookie,opts.cookie);var globals="name,containerClass,zIndex,scrollToBookmarkOnLoad,resizeWithWindow,resizeWithWindowDelay,resizeWithWindowMaxDelay,"+"onresizeall,onresizeall_start,onresizeall_end,onload,onload_start,onload_end,onunload,onunload_start,onunload_end,autoBindCustomButtons,useStateCookie";$.each(globals.split(","),
function(i,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(i,key){delete opts.defaults[key]});$.extend(true,options.defaults,opts.defaults);_c.center=$.extend(true,{},_c.panes,_c.center);var z=options.zIndex;if(z===0||z>0){_c.zIndex.pane_normal=z;_c.zIndex.resizer_normal=z+1;_c.zIndex.iframe_mask=z+1}$.extend(options.center,
opts.center);var o_Center=$.extend(true,{},options.defaults,opts.defaults,options.center);var optionsCenter=("paneClass,contentSelector,applyDemoStyles,triggerEventsOnLoad,showOverflowOnHover,"+"onresize,onresize_start,onresize_end,resizeNestedLayout,resizeContentWhileDragging,"+"onsizecontent,onsizecontent_start,onsizecontent_end").split(",");$.each(optionsCenter,function(i,key){options.center[key]=o_Center[key]});var o,defs=options.defaults;$.each(_c.borderPanes.split(","),function(i,pane){_c[pane]=
$.extend(true,{},_c.panes,_c[pane]);o=options[pane]=$.extend(true,{},options.defaults,options[pane],opts.defaults,opts[pane]);if(!o.paneClass)o.paneClass="ui-layout-pane";if(!o.resizerClass)o.resizerClass="ui-layout-resizer";if(!o.togglerClass)o.togglerClass="ui-layout-toggler";$.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"})});function renameOpts(O){for(var key in newOpts)if(O[key]!=undefined){O[newOpts[key]]=O[key];delete O[key]}}};var getPane=function(pane){var sel=options[pane].paneSelector;if(sel.substr(0,1)==="#")return $Container.find(sel).eq(0);else{var $P=$Container.children(sel).eq(0);return $P.length?$P:$Container.children("form:first").children(sel).eq(0)}};
var initPanes=function(){$.each(_c.allPanes.split(","),function(idx,pane){addPane(pane)});initHandles();$.each(_c.borderPanes.split(","),function(i,pane){if($Ps[pane]&&state[pane].isVisible){setSizeLimits(pane);makePaneFit(pane)}});sizeMidPanes("center");$.each(_c.allPanes.split(","),function(i,pane){var o=options[pane];if($Ps[pane]&&state[pane].isVisible){if(o.triggerEventsOnLoad)_execCallback(pane,o.onresize_end||o.onresize);resizeNestedLayout(pane)}});if($Container.innerHeight()<2)alert(lang.errContainerHeight.replace(/CONTAINER/,
sC.ref))};var addPane=function(pane){var o=options[pane],s=state[pane],c=_c[pane],fx=s.fx,dir=c.dir,spacing=o.spacing_open||0,isCenter=pane=="center",CSS={},$P=$Ps[pane],size,minSize,maxSize;if($P)removePane(pane);else $Cs[pane]=false;$P=$Ps[pane]=getPane(pane);if(!$P.length){$Ps[pane]=false;return}if(!$P.data("layoutCSS")){var props="position,top,left,bottom,right,width,height,overflow,zIndex,display,backgroundColor,padding,margin,border";$P.data("layoutCSS",getElemCSS($P,props))}$P.data("parentLayout",
Instance).data("layoutRole","pane").data("layoutEdge",pane).css(c.cssReq).css("zIndex",_c.zIndex.pane_normal).css(o.applyDemoStyles?c.cssDemo:{}).addClass(o.paneClass+" "+o.paneClass+"-"+pane).bind("mouseenter."+sID,addHover).bind("mouseleave."+sID,removeHover);initContent(pane,false);if(!isCenter){size=s.size=_parseSize(pane,o.size);minSize=_parseSize(pane,o.minSize)||1;maxSize=_parseSize(pane,o.maxSize)||1E5;if(size>0)size=max(min(size,maxSize),minSize);s.isClosed=false;s.isSliding=false;s.isResizing=
false;s.isHidden=false}s.tagName=$P.attr("tagName");s.edge=pane;s.noRoom=false;s.isVisible=true;switch(pane){case "north":CSS.top=sC.insetTop;CSS.left=sC.insetLeft;CSS.right=sC.insetRight;break;case "south":CSS.bottom=sC.insetBottom;CSS.left=sC.insetLeft;CSS.right=sC.insetRight;break;case "west":CSS.left=sC.insetLeft;break;case "east":CSS.right=sC.insetRight;break;case "center":}if(dir=="horz")CSS.height=max(1,cssH(pane,size));else if(dir=="vert")CSS.width=max(1,cssW(pane,size));$P.css(CSS);if(dir!=
"horz")sizeMidPanes(pane,true);if(!s.noRoom)$P.css({visibility:"visible",display:"block"});if(o.initClosed&&o.closable)close(pane,true,true);else if(o.initHidden||o.initClosed)hide(pane);if(o.showOverflowOnHover)$P.hover(allowOverflow,resetOverflow);if(state.initialized){initHandles(pane);initHotkeys(pane);resizeAll();if(s.isVisible){if(o.triggerEventsOnLoad)_execCallback(pane,o.onresize_end||o.onresize);resizeNestedLayout(pane)}}};var initHandles=function(panes){if(!panes||panes=="all")panes=_c.borderPanes;
$.each(panes.split(","),function(i,pane){var $P=$Ps[pane];$Rs[pane]=false;$Ts[pane]=false;if(!$P)return;var o=options[pane],s=state[pane],c=_c[pane],rClass=o.resizerClass,tClass=o.togglerClass,side=c.side.toLowerCase(),spacing=s.isVisible?o.spacing_open:o.spacing_closed,_pane="-"+pane,_state=s.isVisible?"-open":"-closed",$R=$Rs[pane]=$("<div></div>"),$T=o.closable?$Ts[pane]=$("<div></div>"):false;if(!s.isVisible&&o.slidable)$R.attr("title",o.sliderTip).css("cursor",o.sliderCursor);$R.attr("id",o.paneSelector.substr(0,
1)=="#"?o.paneSelector.substr(1)+"-resizer":"").data("parentLayout",Instance).data("layoutRole","resizer").data("layoutEdge",pane).css(_c.resizers.cssReq).css("zIndex",_c.zIndex.resizer_normal).css(o.applyDemoStyles?_c.resizers.cssDemo:{}).addClass(rClass+" "+rClass+_pane).appendTo($Container);if($T){$T.attr("id",o.paneSelector.substr(0,1)=="#"?o.paneSelector.substr(1)+"-toggler":"").data("parentLayout",Instance).data("layoutRole","toggler").data("layoutEdge",pane).css(_c.togglers.cssReq).css(o.applyDemoStyles?
_c.togglers.cssDemo:{}).addClass(tClass+" "+tClass+_pane).appendTo($R);if(o.togglerContent_open)$("<span>"+o.togglerContent_open+"</span>").data("layoutRole","togglerContent").data("layoutEdge",pane).addClass("content content-open").css("display","none").appendTo($T);if(o.togglerContent_closed)$("<span>"+o.togglerContent_closed+"</span>").data("layoutRole","togglerContent").data("layoutEdge",pane).addClass("content content-closed").css("display","none").appendTo($T);enableClosable(pane)}initResizable(pane);
if(s.isVisible)setAsOpen(pane);else{setAsClosed(pane);bindStartSlidingEvent(pane,true)}});sizeHandles("all")};var initContent=function(pane,resize){var o=options[pane],sel=o.contentSelector,$P=$Ps[pane],$C;if(sel)$C=$Cs[pane]=o.findNestedContent?$P.find(sel).eq(0):$P.children(sel).eq(0);if($C&&$C.length){if(!$C.data("layoutCSS"))$C.data("layoutCSS",getElemCSS($C,"height"));$C.css(_c.content.cssReq);if(o.applyDemoStyles){$C.css(_c.content.cssDemo);$P.css(_c.content.cssDemoPane)}state[pane].content=
{};if(resize!==false)sizeContent(pane)}else $Cs[pane]=false};var initButtons=function(){var pre="ui-layout-button-",name;$.each("toggle,open,close,pin,toggle-slide,open-slide".split(","),function(i,action){$.each(_c.borderPanes.split(","),function(ii,pane){$("."+pre+action+"-"+pane).each(function(){name=$(this).data("layoutName")||$(this).attr("layoutName");if(name==undefined||name==options.name)bindButton(this,action,pane)})})})};var initResizable=function(panes){var draggingAvailable=typeof $.fn.draggable==
"function",$Frames,side;if(!panes||panes=="all")panes=_c.borderPanes;$.each(panes.split(","),function(idx,pane){var o=options[pane],s=state[pane],c=_c[pane],side=c.dir=="horz"?"top":"left",r,live;if(!draggingAvailable||!$Ps[pane]||!o.resizable){o.resizable=false;return true}var $P=$Ps[pane],$R=$Rs[pane],base=o.resizerClass,resizerClass=base+"-drag",resizerPaneClass=base+"-"+pane+"-drag",helperClass=base+"-dragging",helperPaneClass=base+"-"+pane+"-dragging",helperLimitClass=base+"-dragging-limit",
helperPaneLimitClass=base+"-"+pane+"-dragging-limit",helperClassesSet=false;if(!s.isClosed)$R.attr("title",o.resizerTip).css("cursor",o.resizerCursor);$R.bind("mouseenter."+sID,onResizerEnter).bind("mouseleave."+sID,onResizerLeave);$R.draggable({containment:$Container[0],axis:c.dir=="horz"?"y":"x",delay:0,distance:1,helper:"clone",opacity:o.resizerDragOpacity,addClasses:false,zIndex:_c.zIndex.resizer_drag,start:function(e,ui){o=options[pane];s=state[pane];live=o.resizeWhileDragging;if(false===_execCallback(pane,
o.ondrag_start))return false;_c.isLayoutBusy=true;s.isResizing=true;timer.clear(pane+"_closeSlider");setSizeLimits(pane);r=s.resizerPosition;$R.addClass(resizerClass+" "+resizerPaneClass);helperClassesSet=false;$Frames=$(o.maskIframesOnResize===true?"iframe":o.maskIframesOnResize).filter(":visible");var id,i=0;$Frames.each(function(){id="ui-layout-mask-"+ ++i;$(this).data("layoutMaskID",id);$('<div id="'+id+'" class="ui-layout-mask ui-layout-mask-'+pane+'"/>').css({background:"#fff",opacity:"0.001",
zIndex:_c.zIndex.iframe_mask,position:"absolute",width:this.offsetWidth+"px",height:this.offsetHeight+"px"}).css($(this).position()).appendTo(this.parentNode)});$("body").disableSelection()},drag:function(e,ui){if(!helperClassesSet){ui.helper.addClass(helperClass+" "+helperPaneClass).css({right:"auto",bottom:"auto"}).children().css("visibility","hidden");helperClassesSet=true;if(s.isSliding)$Ps[pane].css("zIndex",_c.zIndex.pane_sliding)}var limit=0;if(ui.position[side]<r.min){ui.position[side]=r.min;
limit=-1}else if(ui.position[side]>r.max){ui.position[side]=r.max;limit=1}if(limit){ui.helper.addClass(helperLimitClass+" "+helperPaneLimitClass);window.defaultStatus="Panel has reached its "+(limit>0&&pane.match(/north|west/)||limit<0&&pane.match(/south|east/)?"maximum":"minimum")+" size"}else{ui.helper.removeClass(helperLimitClass+" "+helperPaneLimitClass);window.defaultStatus=""}if(live)resizePanes(e,ui,pane)},stop:function(e,ui){$("body").enableSelection();window.defaultStatus="";$R.removeClass(resizerClass+
" "+resizerPaneClass);s.isResizing=false;_c.isLayoutBusy=false;resizePanes(e,ui,pane,true)}});var resizePanes=function(evt,ui,pane,resizingDone){var dragPos=ui.position,c=_c[pane],resizerPos,newSize,i=0;switch(pane){case "north":resizerPos=dragPos.top;break;case "west":resizerPos=dragPos.left;break;case "south":resizerPos=sC.offsetHeight-dragPos.top-o.spacing_open;break;case "east":resizerPos=sC.offsetWidth-dragPos.left-o.spacing_open;break}if(resizingDone){$("div.ui-layout-mask").each(function(){this.parentNode.removeChild(this)});
if(false===_execCallback(pane,o.ondrag_end||o.ondrag))return false}else $Frames.each(function(){$("#"+$(this).data("layoutMaskID")).css($(this).position()).css({width:this.offsetWidth+"px",height:this.offsetHeight+"px"})});newSize=resizerPos-sC["inset"+c.side];manualSizePane(pane,newSize)}})};var destroy=function(){$(window).unbind("."+sID);$(document).unbind("."+sID);$.each(_c.allPanes.split(","),function(i,pane){removePane(pane,false,true)});var $C=$Container.removeData("layout").removeData("layoutContainer").removeClass(options.containerClass);
if(!$C.data("layoutEdge")&&$C.data("layoutCSS"))$C.css($C.data("layoutCSS")).removeData("layoutCSS");if(sC.tagName=="BODY"&&($C=$("html")).data("layoutCSS"))$C.css($C.data("layoutCSS")).removeData("layoutCSS");unload()};var removePane=function(pane,remove,skipResize){if(!$Ps[pane])return;var $P=$Ps[pane],$C=$Cs[pane],$R=$Rs[pane],$T=$Ts[pane],_open="-open",_sliding="-sliding",_closed="-closed",root=options[pane].paneClass,pRoot=root+"-"+pane,classes=[root,root+_open,root+_closed,root+_sliding,pRoot,
pRoot+_open,pRoot+_closed,pRoot+_sliding];$.merge(classes,getHoverClasses($P,true));if(!$P||!$P.length);else if(remove&&!$P.data("layoutContainer")&&(!$C||!$C.length||!$C.data("layoutContainer")))$P.remove();else{$P.removeClass(classes.join(" ")).removeData("layoutParent").removeData("layoutRole").removeData("layoutEdge").removeData("autoHidden").unbind("."+sID);if(!$P.data("layoutContainer"))$P.css($P.data("layoutCSS")).removeData("layoutCSS");if($C&&$C.length&&!$C.data("layoutContainer"))$C.css($C.data("layoutCSS")).removeData("layoutCSS")}if($T&&
$T.length)$T.remove();if($R&&$R.length)$R.remove();$Ps[pane]=$Cs[pane]=$Rs[pane]=$Ts[pane]=false;if(!skipResize){resizeAll();state[pane]={}}};var hide=function(pane,noAnimation){var o=options[pane],s=state[pane],$P=$Ps[pane],$R=$Rs[pane];if(!$P||s.isHidden)return;if(state.initialized&&false===_execCallback(pane,o.onhide_start))return;s.isSliding=false;if($R)$R.hide();if(!state.initialized||s.isClosed){s.isClosed=true;s.isHidden=true;s.isVisible=false;$P.hide();sizeMidPanes(_c[pane].dir=="horz"?"all":
"center");if(state.initialized||o.triggerEventsOnLoad)_execCallback(pane,o.onhide_end||o.onhide)}else{s.isHiding=true;close(pane,false,noAnimation)}};var show=function(pane,openPane,noAnimation,noAlert){var o=options[pane],s=state[pane],$P=$Ps[pane],$R=$Rs[pane];if(!$P||!s.isHidden)return;if(false===_execCallback(pane,o.onshow_start))return;s.isSliding=false;s.isShowing=true;if(openPane===false)close(pane,true);else open(pane,false,noAnimation,noAlert)};var toggle=function(pane,slide){if(!isStr(pane)){pane.stopImmediatePropagation();
pane=$(this).data("layoutEdge")}var s=state[str(pane)];if(s.isHidden)show(pane);else if(s.isClosed)open(pane,!!slide);else close(pane)};var _closePane=function(pane,setHandles){var $P=$Ps[pane],s=state[pane];$P.hide();s.isClosed=true;s.isVisible=false};var close=function(pane,force,noAnimation,skipCallback){if(!state.initialized){_closePane(pane);return}var $P=$Ps[pane],$R=$Rs[pane],$T=$Ts[pane],o=options[pane],s=state[pane],doFX=!noAnimation&&!s.isClosed&&o.fxName_close!="none",isShowing=s.isShowing,
isHiding=s.isHiding,wasSliding=s.isSliding;delete s.isShowing;delete s.isHiding;if(!$P||!o.closable&&!isShowing&&!isHiding)return;else if(!force&&s.isClosed&&!isShowing)return;if(_c.isLayoutBusy){_queue("close",pane,force);return}if(!isShowing&&false===_execCallback(pane,o.onclose_start))return;_c[pane].isMoving=true;_c.isLayoutBusy=true;s.isClosed=true;s.isVisible=false;if(isHiding)s.isHidden=true;else if(isShowing)s.isHidden=false;if(s.isSliding)bindStopSlidingEvents(pane,false);else sizeMidPanes(_c[pane].dir==
"horz"?"all":"center",false);setAsClosed(pane);if(doFX){lockPaneForFX(pane,true);$P.hide(o.fxName_close,o.fxSettings_close,o.fxSpeed_close,function(){lockPaneForFX(pane,false);close_2()})}else{$P.hide();close_2()}function close_2(){if(s.isClosed){bindStartSlidingEvent(pane,true);var altPane=_c.altSide[pane];if(state[altPane].noRoom){setSizeLimits(altPane);makePaneFit(altPane)}if(!skipCallback&&(state.initialized||o.triggerEventsOnLoad)){if(!isShowing)_execCallback(pane,o.onclose_end||o.onclose);if(isShowing)_execCallback(pane,
o.onshow_end||o.onshow);if(isHiding)_execCallback(pane,o.onhide_end||o.onhide)}}_dequeue(pane)}};var setAsClosed=function(pane){var $P=$Ps[pane],$R=$Rs[pane],$T=$Ts[pane],o=options[pane],s=state[pane],side=_c[pane].side.toLowerCase(),inset="inset"+_c[pane].side,rClass=o.resizerClass,tClass=o.togglerClass,_pane="-"+pane,_open="-open",_sliding="-sliding",_closed="-closed";$R.css(side,sC[inset]).removeClass(rClass+_open+" "+rClass+_pane+_open).removeClass(rClass+_sliding+" "+rClass+_pane+_sliding).addClass(rClass+
_closed+" "+rClass+_pane+_closed).unbind("dblclick."+sID);if(o.resizable&&typeof $.fn.draggable=="function")$R.draggable("disable").removeClass("ui-state-disabled").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);$T.children(".content-open").hide();$T.children(".content-closed").css("display","block")}syncPinBtns(pane,false);if(state.initialized)sizeHandles("all")};
var open=function(pane,slide,noAnimation,noAlert){var $P=$Ps[pane],$R=$Rs[pane],$T=$Ts[pane],o=options[pane],s=state[pane],doFX=!noAnimation&&s.isClosed&&o.fxName_open!="none",isShowing=s.isShowing;delete s.isShowing;if(!$P||!o.resizable&&!o.closable&&!isShowing)return;else if(s.isVisible&&!s.isSliding)return;if(s.isHidden&&!isShowing){show(pane,true);return}if(_c.isLayoutBusy){_queue("open",pane,slide);return}setSizeLimits(pane,slide);if(false===_execCallback(pane,o.onopen_start))return;if(s.minSize>
s.maxSize){syncPinBtns(pane,false);if(!noAlert&&o.noRoomToOpenTip)alert(o.noRoomToOpenTip);return}_c[pane].isMoving=true;_c.isLayoutBusy=true;if(slide)bindStopSlidingEvents(pane,true);else if(s.isSliding)bindStopSlidingEvents(pane,false);else if(o.slidable)bindStartSlidingEvent(pane,false);s.noRoom=false;makePaneFit(pane);s.isVisible=true;s.isClosed=false;if(isShowing)s.isHidden=false;if(doFX){lockPaneForFX(pane,true);$P.show(o.fxName_open,o.fxSettings_open,o.fxSpeed_open,function(){lockPaneForFX(pane,
false);open_2()})}else{$P.show();open_2()}function open_2(){if(s.isVisible){_fixIframe(pane);if(!s.isSliding)sizeMidPanes(_c[pane].dir=="vert"?"center":"all",false);setAsOpen(pane)}_dequeue(pane)}};var setAsOpen=function(pane,skipCallback){var $P=$Ps[pane],$R=$Rs[pane],$T=$Ts[pane],o=options[pane],s=state[pane],side=_c[pane].side.toLowerCase(),inset="inset"+_c[pane].side,rClass=o.resizerClass,tClass=o.togglerClass,_pane="-"+pane,_open="-open",_closed="-closed",_sliding="-sliding";$R.css(side,sC[inset]+
getPaneSize(pane)).removeClass(rClass+_closed+" "+rClass+_pane+_closed).addClass(rClass+_open+" "+rClass+_pane+_open);if(s.isSliding)$R.addClass(rClass+_sliding+" "+rClass+_pane+_sliding);else $R.removeClass(rClass+_sliding+" "+rClass+_pane+_sliding);if(o.resizerDblClickToggle)$R.bind("dblclick",toggle);removeHover(0,$R);if(o.resizable&&typeof $.fn.draggable=="function")$R.draggable("enable").css("cursor",o.resizerCursor).attr("title",o.resizerTip);else if(!s.isSliding)$R.css("cursor","default");
if($T){$T.removeClass(tClass+_closed+" "+tClass+_pane+_closed).addClass(tClass+_open+" "+tClass+_pane+_open).attr("title",o.togglerTip_open);removeHover(0,$T);$T.children(".content-closed").hide();$T.children(".content-open").css("display","block")}syncPinBtns(pane,!s.isSliding);$.extend(s,getElemDims($P));if(state.initialized){sizeHandles("all");sizeContent(pane,true)}if(!skipCallback&&(state.initialized||o.triggerEventsOnLoad)&&$P.is(":visible")){_execCallback(pane,o.onopen_end||o.onopen);if(s.isShowing)_execCallback(pane,
o.onshow_end||o.onshow);if(state.initialized){_execCallback(pane,o.onresize_end||o.onresize);resizeNestedLayout(pane)}}};var slideOpen=function(evt_or_pane){var evt=isStr(evt_or_pane)?null:evt_or_pane,pane=evt?$(this).data("layoutEdge"):evt_or_pane,s=state[pane],delay=options[pane].slideDelay_open;if(evt)evt.stopImmediatePropagation();if(s.isClosed&&evt&&evt.type=="mouseenter"&&delay>0)timer.set(pane+"_openSlider",open_NOW,delay);else open_NOW();function open_NOW(evt){if(!s.isClosed)bindStopSlidingEvents(pane,
true);else if(!_c[pane].isMoving)open(pane,true)}};var slideClose=function(evt_or_pane){var evt=isStr(evt_or_pane)?null:evt_or_pane,pane=evt?$(this).data("layoutEdge"):evt_or_pane,o=options[pane],s=state[pane],delay=_c[pane].isMoving?1E3:300;if(s.isClosed||s.isResizing)return;else if(o.slideTrigger_close=="click")close_NOW();else if(o.preventQuickSlideClose&&_c.isLayoutBusy)return;else if(o.preventPrematureSlideClose&&evt&&$.layout.isMouseOverElem(evt,$Ps[pane]))return;else if(evt)timer.set(pane+
"_closeSlider",close_NOW,max(o.slideDelay_close,delay));else close_NOW();function close_NOW(){if(s.isClosed)bindStopSlidingEvents(pane,false);else if(!_c[pane].isMoving)close(pane)}};var slideToggle=function(pane){toggle(pane,true)};var lockPaneForFX=function(pane,doLock){var $P=$Ps[pane];if(doLock){$P.css({zIndex:_c.zIndex.pane_animate});if(pane=="south")$P.css({top:sC.insetTop+sC.innerHeight-$P.outerHeight()});else if(pane=="east")$P.css({left:sC.insetLeft+sC.innerWidth-$P.outerWidth()})}else{$P.css({zIndex:state[pane].isSliding?
_c.zIndex.pane_sliding:_c.zIndex.pane_normal});if(pane=="south")$P.css({top:"auto"});else if(pane=="east")$P.css({left:"auto"});var o=options[pane];if(state.browser.msie&&o.fxOpacityFix&&o.fxName_open!="slide"&&$P.css("filter")&&$P.css("opacity")==1)$P[0].style.removeAttribute("filter")}};var bindStartSlidingEvent=function(pane,enable){var o=options[pane],$P=$Ps[pane],$R=$Rs[pane],trigger=o.slideTrigger_open.toLowerCase();if(!$R||enable&&!o.slidable)return;if(trigger.match(/mouseover/))trigger=o.slideTrigger_open=
"mouseenter";else if(!trigger.match(/click|dblclick|mouseenter/))trigger=o.slideTrigger_open="click";$R[enable?"bind":"unbind"](trigger+"."+sID,slideOpen).css("cursor",enable?o.sliderCursor:"default").attr("title",enable?o.sliderTip:"")};var bindStopSlidingEvents=function(pane,enable){var o=options[pane],s=state[pane],z=_c.zIndex,trigger=o.slideTrigger_close.toLowerCase(),action=enable?"bind":"unbind",$P=$Ps[pane],$R=$Rs[pane];s.isSliding=enable;timer.clear(pane+"_closeSlider");if(enable)bindStartSlidingEvent(pane,
false);$P.css("zIndex",enable?z.pane_sliding:z.pane_normal);$R.css("zIndex",enable?z.pane_sliding:z.resizer_normal);if(!trigger.match(/click|mouseleave/))trigger=o.slideTrigger_close="mouseleave";$R[action](trigger,slideClose);if(trigger=="mouseleave"){$P[action]("mouseleave."+sID,slideClose);$R[action]("mouseenter."+sID,cancelMouseOut);$P[action]("mouseenter."+sID,cancelMouseOut)}if(!enable)timer.clear(pane+"_closeSlider");else if(trigger=="click"&&!o.resizable){$R.css("cursor",enable?o.sliderCursor:
"default");$R.attr("title",enable?o.togglerTip_open:"")}function cancelMouseOut(evt){timer.clear(pane+"_closeSlider");evt.stopPropagation()}};var makePaneFit=function(pane,isOpening,skipCallback,force){var o=options[pane],s=state[pane],c=_c[pane],$P=$Ps[pane],$R=$Rs[pane],isSidePane=c.dir=="vert",hasRoom=false;if(pane=="center"||isSidePane&&s.noVerticalRoom){hasRoom=s.maxHeight>0;if(hasRoom&&s.noRoom){$P.show();if($R)$R.show();s.isVisible=true;s.noRoom=false;if(isSidePane)s.noVerticalRoom=false;_fixIframe(pane)}else if(!hasRoom&&
!s.noRoom){$P.hide();if($R)$R.hide();s.isVisible=false;s.noRoom=true}}if(pane=="center");else if(s.minSize<=s.maxSize){hasRoom=true;if(s.size>s.maxSize)sizePane(pane,s.maxSize,skipCallback,force);else if(s.size<s.minSize)sizePane(pane,s.minSize,skipCallback,force);else if($R&&$P.is(":visible")){var side=c.side.toLowerCase(),pos=s.size+sC["inset"+c.side];if(_cssNum($R,side)!=pos)$R.css(side,pos)}if(s.noRoom)if(s.wasOpen&&o.closable)if(o.autoReopen)open(pane,false,true,true);else s.noRoom=false;else show(pane,
s.wasOpen,true,true)}else if(!s.noRoom){s.noRoom=true;s.wasOpen=!s.isClosed&&!s.isSliding;if(s.isClosed);else if(o.closable)close(pane,true,true);else hide(pane,true)}};var manualSizePane=function(pane,size,skipCallback){var o=options[pane],forceResize=o.resizeWhileDragging&&!_c.isLayoutBusy;o.autoResize=false;sizePane(pane,size,skipCallback,forceResize)};var sizePane=function(pane,size,skipCallback,force){var o=options[pane],s=state[pane],$P=$Ps[pane],$R=$Rs[pane],side=_c[pane].side.toLowerCase(),
inset="inset"+_c[pane].side,skipResizeWhileDragging=_c.isLayoutBusy&&!o.triggerEventsWhileDragging,oldSize;setSizeLimits(pane);oldSize=s.size;size=_parseSize(pane,size);size=max(size,_parseSize(pane,o.minSize));size=min(size,s.maxSize);if(size<s.minSize){makePaneFit(pane,false,skipCallback);return}if(!force&&size==oldSize)return;if(!skipCallback&&state.initialized&&s.isVisible)_execCallback(pane,o.onresize_start);$P.css(_c[pane].sizeType.toLowerCase(),max(1,cssSize(pane,size)));s.size=size;$.extend(s,
getElemDims($P));if($R&&$P.is(":visible"))$R.css(side,size+sC[inset]);sizeContent(pane);if(!skipCallback&&!skipResizeWhileDragging&&state.initialized&&s.isVisible){_execCallback(pane,o.onresize_end||o.onresize);resizeNestedLayout(pane)}if(!skipCallback){if(!s.isSliding)sizeMidPanes(_c[pane].dir=="horz"?"all":"center",skipResizeWhileDragging,force);sizeHandles("all")}var altPane=_c.altSide[pane];if(size<oldSize&&state[altPane].noRoom){setSizeLimits(altPane);makePaneFit(altPane,false,skipCallback)}};
var sizeMidPanes=function(panes,skipCallback,force){if(!panes||panes=="all")panes="east,west,center";$.each(panes.split(","),function(i,pane){if(!$Ps[pane])return;var o=options[pane],s=state[pane],$P=$Ps[pane],$R=$Rs[pane],isCenter=pane=="center",hasRoom=true,CSS={},d=calcNewCenterPaneDims();$.extend(s,getElemDims($P));if(pane=="center"){if(!force&&s.isVisible&&d.width==s.outerWidth&&d.height==s.outerHeight)return true;$.extend(s,cssMinDims(pane),{maxWidth:d.width,maxHeight:d.height});CSS=d;CSS.width=
cssW(pane,d.width);CSS.height=cssH(pane,d.height);hasRoom=CSS.width>0&&CSS.height>0;if(!hasRoom&&!state.initialized&&o.minWidth>0){var reqPx=o.minWidth-s.outerWidth,minE=options.east.minSize||0,minW=options.west.minSize||0,sizeE=state.east.size,sizeW=state.west.size,newE=sizeE,newW=sizeW;if(reqPx>0&&state.east.isVisible&&sizeE>minE){newE=max(sizeE-minE,sizeE-reqPx);reqPx-=sizeE-newE}if(reqPx>0&&state.west.isVisible&&sizeW>minW){newW=max(sizeW-minW,sizeW-reqPx);reqPx-=sizeW-newW}if(reqPx==0){if(sizeE!=
minE)sizePane("east",newE,true);if(sizeW!=minW)sizePane("west",newW,true);sizeMidPanes("center",skipCallback,force);return}}}else{if(s.isVisible&&!s.noVerticalRoom)$.extend(s,getElemDims($P),cssMinDims(pane));if(!force&&!s.noVerticalRoom&&d.height==s.outerHeight)return true;CSS.top=d.top;CSS.bottom=d.bottom;CSS.height=cssH(pane,d.height);s.maxHeight=max(0,CSS.height);hasRoom=s.maxHeight>0;if(!hasRoom)s.noVerticalRoom=true}if(hasRoom){if(!skipCallback&&state.initialized)_execCallback(pane,o.onresize_start);
$P.css(CSS);if(s.noRoom&&!s.isClosed&&!s.isHidden)makePaneFit(pane);if(s.isVisible){$.extend(s,getElemDims($P));if(state.initialized)sizeContent(pane)}}else if(!s.noRoom&&s.isVisible)makePaneFit(pane);if(!s.isVisible)return true;if(pane=="center"){var b=state.browser;var fix=b.isIE6||b.msie&&!b.boxModel;if($Ps.north&&(fix||state.north.tagName=="IFRAME"))$Ps.north.css("width",cssW($Ps.north,sC.innerWidth));if($Ps.south&&(fix||state.south.tagName=="IFRAME"))$Ps.south.css("width",cssW($Ps.south,sC.innerWidth))}if(!skipCallback&&
state.initialized){_execCallback(pane,o.onresize_end||o.onresize);resizeNestedLayout(pane)}})};var resizeAll=function(){var oldW=sC.innerWidth,oldH=sC.innerHeight;$.extend(state.container,getElemDims($Container));if(!sC.outerHeight)return;if(false===_execCallback(null,options.onresizeall_start))return false;var shrunkH=sC.innerHeight<oldH,shrunkW=sC.innerWidth<oldW,$P,o,s,dir;$.each(["south","north","east","west"],function(i,pane){if(!$Ps[pane])return;s=state[pane];o=options[pane];dir=_c[pane].dir;
if(o.autoResize&&s.size!=o.size)sizePane(pane,o.size,true,true);else{setSizeLimits(pane);makePaneFit(pane,false,true,true)}});sizeMidPanes("all",true,true);sizeHandles("all");o=options;$.each(_c.allPanes.split(","),function(i,pane){$P=$Ps[pane];if(!$P)return;if(state[pane].isVisible){_execCallback(pane,o[pane].onresize_end||o[pane].onresize);resizeNestedLayout(pane)}});_execCallback(null,o.onresizeall_end||o.onresizeall)};var resizeNestedLayout=function(pane){var $P=$Ps[pane],$C=$Cs[pane],d="layoutContainer";
if(options[pane].resizeNestedLayout)if($P.data(d))$P.layout().resizeAll();else if($C&&$C.data(d))$C.layout().resizeAll()};var sizeContent=function(panes,remeasure){if(!panes||panes=="all")panes=_c.allPanes;$.each(panes.split(","),function(idx,pane){var $P=$Ps[pane],$C=$Cs[pane],o=options[pane],s=state[pane],m=s.content;if(!$P||!$C||!$P.is(":visible"))return true;if(false===_execCallback(null,o.onsizecontent_start))return;if(!_c.isLayoutBusy||m.top==undefined||remeasure||o.resizeContentWhileDragging){_measure();
if(m.hiddenFooters>0&&$P.css("overflow")=="hidden"){$P.css("overflow","visible");_measure();$P.css("overflow","hidden")}}var newH=s.innerHeight-(m.spaceAbove-s.css.paddingTop)-(m.spaceBelow-s.css.paddingBottom);if(!$C.is(":visible")||m.height!=newH){setOuterHeight($C,newH,true);m.height=newH}if(state.initialized){_execCallback(pane,o.onsizecontent_end||o.onsizecontent);resizeNestedLayout(pane)}function _below($E){return max(s.css.paddingBottom,parseInt($E.css("marginBottom"),10)||0)}function _measure(){var ignore=
options[pane].contentIgnoreSelector,$Fs=$C.nextAll().not(ignore||":lt(0)"),$Fs_vis=$Fs.filter(":visible"),$F=$Fs_vis.filter(":last");m={top:$C[0].offsetTop,height:$C.outerHeight(),numFooters:$Fs.length,hiddenFooters:$Fs.length-$Fs_vis.length,spaceBelow:0};m.spaceAbove=m.top;m.bottom=m.top+m.height;if($F.length)m.spaceBelow=$F[0].offsetTop+$F.outerHeight()-m.bottom+_below($F);else m.spaceBelow=_below($C)}})};var sizeHandles=function(panes){if(!panes||panes=="all")panes=_c.borderPanes;$.each(panes.split(","),
function(i,pane){var o=options[pane],s=state[pane],$P=$Ps[pane],$R=$Rs[pane],$T=$Ts[pane],$TC;if(!$P||!$R)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();s.resizerLength=paneLen;$R.css({width:max(1,cssW($R,paneLen)),height:max(0,cssH($R,spacing)),left:_cssNum($P,
"left")})}else{paneLen=$P.outerHeight();s.resizerLength=paneLen;$R.css({height:max(1,cssH($R,paneLen)),width:max(0,cssW($R,spacing)),top:sC.insetTop+getPaneSize("north",true)})}removeHover(o,$R);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(isStr(togAlign))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,10);if(togAlign>=0)offset=x;else offset=paneLen-togLen+x}if(dir=="horz"){var width=cssW($T,togLen);$T.css({width:max(0,width),height:max(1,cssH($T,spacing)),left:offset,top:0});$T.children(".content").each(function(){$TC=$(this);$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,left:0});$T.children(".content").each(function(){$TC=
$(this);$TC.css("marginTop",Math.floor((height-$TC.outerHeight())/2))})}removeHover(0,$T)}if(!state.initialized&&(o.initHidden||s.noRoom)){$R.hide();if($T)$T.hide()}})};var enableClosable=function(pane){var $T=$Ts[pane],o=options[pane];if(!$T)return;o.closable=true;$T.bind("click."+sID,function(evt){evt.stopPropagation();toggle(pane)}).bind("mouseenter."+sID,addHover).bind("mouseleave."+sID,removeHover).css("visibility","visible").css("cursor","pointer").attr("title",state[pane].isClosed?o.togglerTip_closed:
o.togglerTip_open).show()};var disableClosable=function(pane,hide){var $T=$Ts[pane];if(!$T)return;options[pane].closable=false;if(state[pane].isClosed)open(pane,false,true);$T.unbind("."+sID).css("visibility",hide?"hidden":"visible").css("cursor","default").attr("title","")};var enableSlidable=function(pane){var $R=$Rs[pane],o=options[pane];if(!$R||!$R.data("draggable"))return;options[pane].slidable=true;if(s.isClosed)bindStartSlidingEvent(pane,true)};var disableSlidable=function(pane){var $R=$Rs[pane];
if(!$R)return;options[pane].slidable=false;if(state[pane].isSliding)close(pane,false,true);else{bindStartSlidingEvent(pane,false);$R.css("cursor","default").attr("title","");removeHover(null,$R[0])}};var enableResizable=function(pane){var $R=$Rs[pane],o=options[pane];if(!$R||!$R.data("draggable"))return;o.resizable=true;$R.draggable("enable").bind("mouseenter."+sID,onResizerEnter).bind("mouseleave."+sID,onResizerLeave);if(!state[pane].isClosed)$R.css("cursor",o.resizerCursor).attr("title",o.resizerTip)};
var disableResizable=function(pane){var $R=$Rs[pane];if(!$R||!$R.data("draggable"))return;options[pane].resizable=false;$R.draggable("disable").unbind("."+sID).css("cursor","default").attr("title","");removeHover(null,$R[0])};var swapPanes=function(pane1,pane2){state[pane1].edge=pane2;state[pane2].edge=pane1;var cancelled=false;if(false===_execCallback(pane1,options[pane1].onswap_start))cancelled=true;if(!cancelled&&false===_execCallback(pane2,options[pane2].onswap_start))cancelled=true;if(cancelled){state[pane1].edge=
pane1;state[pane2].edge=pane2;return}var oPane1=copy(pane1),oPane2=copy(pane2),sizes={};sizes[pane1]=oPane1?oPane1.state.size:0;sizes[pane2]=oPane2?oPane2.state.size:0;$Ps[pane1]=false;$Ps[pane2]=false;state[pane1]={};state[pane2]={};if($Ts[pane1])$Ts[pane1].remove();if($Ts[pane2])$Ts[pane2].remove();if($Rs[pane1])$Rs[pane1].remove();if($Rs[pane2])$Rs[pane2].remove();$Rs[pane1]=$Rs[pane2]=$Ts[pane1]=$Ts[pane2]=false;move(oPane1,pane2);move(oPane2,pane1);oPane1=oPane2=sizes=null;if($Ps[pane1])$Ps[pane1].css(_c.visible);
if($Ps[pane2])$Ps[pane2].css(_c.visible);resizeAll();_execCallback(pane1,options[pane1].onswap_end||options[pane1].onswap);_execCallback(pane2,options[pane2].onswap_end||options[pane2].onswap);return;function copy(n){var $P=$Ps[n],$C=$Cs[n];return!$P?false:{pane:n,P:$P?$P[0]:false,C:$C?$C[0]:false,state:$.extend({},state[n]),options:$.extend({},options[n])}}function move(oPane,pane){if(!oPane)return;var P=oPane.P,C=oPane.C,oldPane=oPane.pane,c=_c[pane],side=c.side.toLowerCase(),inset="inset"+c.side,
s=$.extend({},state[pane]),o=options[pane],fx={resizerCursor:o.resizerCursor},re,size,pos;$.each("fxName,fxSpeed,fxSettings".split(","),function(i,k){fx[k]=o[k];fx[k+"_open"]=o[k+"_open"];fx[k+"_close"]=o[k+"_close"]});$Ps[pane]=$(P).data("layoutEdge",pane).css(_c.hidden).css(c.cssReq);$Cs[pane]=C?$(C):false;options[pane]=$.extend({},oPane.options,fx);state[pane]=$.extend({},oPane.state);re=new RegExp(o.paneClass+"-"+oldPane,"g");P.className=P.className.replace(re,o.paneClass+"-"+pane);initHandles(pane);
if(c.dir!=_c[oldPane].dir){size=sizes[pane]||0;setSizeLimits(pane);size=max(size,state[pane].minSize);manualSizePane(pane,size,true)}else $Rs[pane].css(side,sC[inset]+(state[pane].isVisible?getPaneSize(pane):0));if(oPane.state.isVisible&&!s.isVisible)setAsOpen(pane,true);else{setAsClosed(pane);bindStartSlidingEvent(pane,true)}oPane=null}};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"},ALT=evt.altKey,SHIFT=
evt.shiftKey,CTRL=evt.ctrlKey,CURSOR=CTRL&&code>=37&&code<=40,o,k,m,pane;if(CURSOR&&options[PANE[code]].enableCursorHotkey)pane=PANE[code];else if(CTRL||SHIFT)$.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||!$Ps[pane]||!options[pane].closable||state[pane].isHidden)return true;toggle(pane);evt.stopPropagation();
evt.returnValue=false;return false}function allowOverflow(el){if(this&&this.tagName)el=this;var $P;if(isStr(el))$P=$Ps[el];else if($(el).data("layoutRole"))$P=$(el);else $(el).parents().each(function(){if($(this).data("layoutRole")){$P=$(this);return false}});if(!$P||!$P.length)return;var pane=$P.data("layoutEdge"),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+2},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.match(/visible|auto/)){curCSS.overflowX=ofX;newCSS.overflowX="visible"}if(ofY&&!ofY.match(/visible|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(el){if(this&&this.tagName)el=this;var $P;if(isStr(el))$P=$Ps[el];else if($(el).data("layoutRole"))$P=
$(el);else $(el).parents().each(function(){if($(this).data("layoutRole")){$P=$(this);return false}});if(!$P||!$P.length)return;var pane=$P.data("layoutEdge"),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);if(!$E.length)alert(lang.errButton+lang.selector+": "+selector);else if(_c.borderPanes.indexOf(pane)==-1)alert(lang.errButton+lang.Pane.toLowerCase()+": "+
pane);else{var btn=options[pane].buttonClass+"-"+action;$E.addClass(btn+" "+btn+"-"+pane).data("layoutName",options.name);return $E}return null}function bindButton(selector,action,pane){switch(action.toLowerCase()){case "toggle":addToggleBtn(selector,pane);break;case "open":addOpenBtn(selector,pane);break;case "close":addCloseBtn(selector,pane);break;case "pin":addPinBtn(selector,pane);break;case "toggle-slide":addToggleBtn(selector,pane,true);break;case "open-slide":addOpenBtn(selector,pane,true);
break}}function addToggleBtn(selector,pane,slide){var $E=getBtn(selector,pane,"toggle");if($E)$E.click(function(evt){toggle(pane,!!slide);evt.stopPropagation()})}function addOpenBtn(selector,pane,slide){var $E=getBtn(selector,pane,"open");if($E)$E.attr("title",lang.Open).click(function(evt){open(pane,!!slide);evt.stopPropagation()})}function addCloseBtn(selector,pane){var $E=getBtn(selector,pane,"close");if($E)$E.attr("title",lang.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 pin=options[pane].buttonClass+"-pin",side=pin+"-"+pane,UP=pin+"-up "+side+"-up",DN=pin+"-down "+side+"-down";$Pin.attr("pin",doPin?"down":"up").attr("title",doPin?lang.Unpin:lang.Pin).removeClass(doPin?UP:DN).addClass(doPin?DN:UP)}function isCookiesEnabled(){return navigator.cookieEnabled!=0}function getCookie(opts){var o=$.extend({},options.cookie,opts||{}),name=o.name||options.name||"Layout",c=document.cookie,cs=c?c.split(";"):[],pair;for(var i=0,n=cs.length;i<n;i++){pair=$.trim(cs[i]).split("=");
if(pair[0]==name)return decodeJSON(decodeURIComponent(pair[1]))}return""}function saveCookie(keys,opts){var o=$.extend({},options.cookie,opts||{}),name=o.name||options.name||"Layout",params="",date="",clear=false;if(o.expires.toUTCString)date=o.expires;else if(typeof o.expires=="number"){date=new Date;if(o.expires>0)date.setDate(date.getDate()+o.expires);else{date.setYear(1970);clear=true}}if(date)params+=";expires="+date.toUTCString();if(o.path)params+=";path="+o.path;if(o.domain)params+=";domain="+
o.domain;if(o.secure)params+=";secure";if(clear){state.cookie={};document.cookie=name+"="+params}else{state.cookie=getState(keys||o.keys);document.cookie=name+"="+encodeURIComponent(encodeJSON(state.cookie))+params}return $.extend({},state.cookie)}function deleteCookie(){saveCookie("",{expires:-1})}function loadCookie(opts){var o=getCookie(opts);if(o){state.cookie=$.extend({},o);loadState(o)}return o}function loadState(opts,animate){$.extend(true,options,opts);if(state.initialized){var pane,o,v,a=
!animate;$.each(_c.allPanes.split(","),function(idx,pane){o=opts[pane];if(typeof o!="object")return;v=o.initHidden;if(v===true)hide(pane,a);if(v===false)show(pane,false,a);v=o.size;if(v>0)sizePane(pane,v);v=o.initClosed;if(v===true)close(pane,false,a);if(v===false)open(pane,false,a)})}}function getState(keys){var data={},alt={isClosed:"initClosed",isHidden:"initHidden"},pair,pane,key,val;if(!keys)keys=options.cookie.keys;if($.isArray(keys))keys=keys.join(",");keys=keys.replace(/__/g,".").split(",");
for(var i=0,n=keys.length;i<n;i++){pair=keys[i].split(".");pane=pair[0];key=pair[1];if(_c.allPanes.indexOf(pane)<0)continue;val=state[pane][key];if(val==undefined)continue;if(key=="isClosed"&&state[pane]["isSliding"])val=true;(data[pane]||(data[pane]={}))[alt[key]?alt[key]:key]=val}return data}function encodeJSON(JSON){return parse(JSON);function parse(h){var D=[],i=0,k,v,t;for(k in h){v=h[k];t=typeof v;if(t=="string")v='"'+v+'"';else if(t=="object")v=parse(v);D[i++]='"'+k+'":'+v}return"{"+D.join(",")+
"}"}}function decodeJSON(str){try{return window["eval"]("("+str+")")||{}}catch(e){return{}}}var $Container=$(this).eq(0);if(!$Container.length)return null;if($Container.data("layoutContainer")&&$Container.data("layout"))return $Container.data("layout");var $Ps={},$Cs={},$Rs={},$Ts={},sC=state.container,sID=state.id;var Instance={options:options,state:state,container:$Container,panes:$Ps,contents:$Cs,resizers:$Rs,togglers:$Ts,toggle:toggle,hide:hide,show:show,open:open,close:close,slideOpen:slideOpen,
slideClose:slideClose,slideToggle:slideToggle,initContent:initContent,sizeContent:sizeContent,sizePane:manualSizePane,swapPanes:swapPanes,resizeAll:resizeAll,destroy:destroy,addPane:addPane,removePane:removePane,setSizeLimits:setSizeLimits,bindButton:bindButton,addToggleBtn:addToggleBtn,addOpenBtn:addOpenBtn,addCloseBtn:addCloseBtn,addPinBtn:addPinBtn,allowOverflow:allowOverflow,resetOverflow:resetOverflow,encodeJSON:encodeJSON,decodeJSON:decodeJSON,getState:getState,getCookie:getCookie,saveCookie:saveCookie,
deleteCookie:deleteCookie,loadCookie:loadCookie,loadState:loadState,cssWidth:cssW,cssHeight:cssH,enableClosable:enableClosable,disableClosable:disableClosable,enableSlidable:enableSlidable,disableSlidable:disableSlidable,enableResizable:enableResizable,disableResizable:disableResizable};_create();return Instance}})(jQuery);

View File

@ -1,140 +0,0 @@
/*
* LAYOUT STATE MANAGEMENT
*
* @requires json2.js - http://www.json.org/json2.js
*
* @example layoutState.options = { layoutName: "myLayout", keys: "west.isClosed,east.isClosed" }
* @example layoutState.save( "myLayout", "west.isClosed,north.size,south.isHidden", {expires: 7} );
* @example layoutState.load( "myLayout" );
* @example layoutState.clear();
* @example var hash_SavedState = layoutState.load();
* @example var hash_SavedState = layoutState.data;
*/
var layoutState = {
options: {
layoutName: 'myLayout' // default name (optional)
// *** IMPORTANT *** specify your keys in same format as your layout options...
/* Sub-Key-Format State Options
, keys: 'north.size,south.size,east.size,west.size,' +
'north.isClosed,south.isClosed,east.isClosed,west.isClosed,' +
'north.isHidden,south.isHidden,east.isHidden,west.isHidden'
*/
// Flat-Format State Options
, keys: 'north__size,south__size,east__size,west__size,' +
'north__isClosed,south__isClosed,east__isClosed,west__isClosed,' +
'north__isHidden,south__isHidden,east__isHidden,west__isHidden'
// Cookie Options
, domain: ''
, path: ''
, expires: '' // 'days' to keep cookie - leave blank for 'session cookie'
, secure: false
}
, data: {}
, clear: function (layoutName) {
this.save( layoutName, 'dummyKey', { expires: -1 });
}
, save: function (layoutName, keys, opts) {
console.log('save');
var
o = jQuery.extend( {}, this.options, opts||{} )
, layout = window[ layoutName || o.layoutName ]
;
if (!keys) keys = o.keys;
if (typeof keys == 'string') keys = keys.split(',');
if (!layout || !layout.state || !keys.length) return false;
var
isNum = typeof o.expires == 'number'
, date = new Date()
, params = ''
, clear = false
;
if (isNum || o.expires.toUTCString) {
if (isNum) {
if (o.expires <= 0) {
date.setYear(1970);
clear = true;
}
else
date.setTime(date.getTime() + (o.expires * 24 * 60 * 60 * 1000));
}
else
date = o.expires;
// use expires attribute, max-age is not supported by IE
params += ';expires='+ date.toUTCString();
}
if (o.path) params += ';path='+ o.path;
if (o.domain) params += ';domain='+ o.domain;
if (o.secure) params += ';secure';
if (clear) {
this.data = {}; // clear the data struct too
document.cookie = (layoutName || o.layoutName) +'='+ params;
}
else {
this.data = readState( layout, keys ); // read current panes-state
document.cookie = (layoutName || o.layoutName) +'='+ encodeURIComponent(JSON.stringify(this.data)) + params;
//alert( 'JSON.stringify(this.data) = '+ (layoutName || o.layoutName) +'='+ JSON.stringify( this.data ) );
}
return this.data;
// SUB-ROUTINE
function readState (layout, keys) {
var
state = layout.state // alias to the 'layout state'
, data = {}
, panes = 'north,south,east,west,center' // validation
, alt = { isClosed: 'initClosed', isHidden: 'initHidden' }
, delim = (keys[0].indexOf('__') > 0 ? '__' : '.')
, pair, pane, key, val
;
for (var i=0; i < keys.length; i++) {
pair = keys[i].split(delim);
pane = pair[0];
key = pair[1];
if (panes.indexOf(pane) < 0) continue; // bad pane!
if (key=='isClosed') // if 'temporarily open' (sliding), then isClosed=false, so...
val = state[ pane ][ key ] || state[ pane ][ 'isSliding' ];
else
val = state[ pane ][ key ];
if (val != undefined) {
if (delim=='.') { // sub-key format
if (!data[ pane ]) data[ pane ] = {};
data[ pane ][ alt[key] ? alt[key] : key ] = val;
}
else // delim = '__' - flat-format
data[ pane + delim + (alt[key] ? alt[key] : key) ] = val;
}
}
return data;
}
}
, load: function (layoutName) {
if (!layoutName) layoutName = this.options.layoutName;
if (!layoutName) return {};
var
data = {}
, c = document.cookie
, cs, pair, i // loop vars
;
if (c && c != '') {
cs = c.split(';');
for (i = 0; i < cs.length; i++) {
c = jQuery.trim(cs[i]);
pair = c.split('='); // name=value pair
if (pair[0] == layoutName) { // this is the layout cookie
data = JSON.parse(decodeURIComponent(pair[1]));
break; // DONE
}
}
}
return (this.data = data);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,150 +0,0 @@
<!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>

View File

@ -1,197 +0,0 @@
<!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
admin/thirdparty/jsizes/.piston.yml vendored Normal file
View File

@ -0,0 +1,8 @@
---
format: 1
handler:
commit: 15dc4845734b83b31fb820131c675a5f0c10db95
branch: master
lock: false
repository_class: Piston::Git::Repository
repository_url: git://github.com/bramstein/jsizes.git

77
admin/thirdparty/jsizes/lib/jquery.sizes.js vendored Executable file
View File

@ -0,0 +1,77 @@
/**
* @preserve JSizes - JQuery plugin v0.33
*
* Licensed under the revised BSD License.
* Copyright 2008-2010 Bram Stein
* All rights reserved.
*/
/*global jQuery*/
(function ($) {
var num = function (value) {
return parseInt(value, 10) || 0;
};
/**
* Sets or gets the values for min-width, min-height, max-width
* and max-height.
*/
$.each(['min', 'max'], function (i, name) {
$.fn[name + 'Size'] = function (value) {
var width, height;
if (value) {
if (value.width !== undefined) {
this.css(name + '-width', value.width);
}
if (value.height !== undefined) {
this.css(name + '-height', value.height);
}
return this;
}
else {
width = this.css(name + '-width');
height = this.css(name + '-height');
// Apparently:
// * Opera returns -1px instead of none
// * IE6 returns undefined instead of none
return {'width': (name === 'max' && (width === undefined || width === 'none' || num(width) === -1) && Number.MAX_VALUE) || num(width),
'height': (name === 'max' && (height === undefined || height === 'none' || num(height) === -1) && Number.MAX_VALUE) || num(height)};
}
};
});
/**
* Returns whether or not an element is visible.
*/
$.fn.isVisible = function () {
return this.is(':visible');
};
/**
* Sets or gets the values for border, margin and padding.
*/
$.each(['border', 'margin', 'padding'], function (i, name) {
$.fn[name] = function (value) {
if (value) {
if (value.top !== undefined) {
this.css(name + '-top' + (name === 'border' ? '-width' : ''), value.top);
}
if (value.bottom !== undefined) {
this.css(name + '-bottom' + (name === 'border' ? '-width' : ''), value.bottom);
}
if (value.left !== undefined) {
this.css(name + '-left' + (name === 'border' ? '-width' : ''), value.left);
}
if (value.right !== undefined) {
this.css(name + '-right' + (name === 'border' ? '-width' : ''), value.right);
}
return this;
}
else {
return {top: num(this.css(name + '-top' + (name === 'border' ? '-width' : ''))),
bottom: num(this.css(name + '-bottom' + (name === 'border' ? '-width' : ''))),
left: num(this.css(name + '-left' + (name === 'border' ? '-width' : ''))),
right: num(this.css(name + '-right' + (name === 'border' ? '-width' : '')))};
}
};
});
}(jQuery));

View File

@ -5,8 +5,6 @@
top: 0; top: 0;
font-size : 14px; font-size : 14px;
font-weight : bold; font-weight : bold;
color: white;
background: #0075C9 url(../images/textures/obar.gif) 0px 0px repeat-x !important;
height : 22px !important; height : 22px !important;
} }
@ -17,7 +15,6 @@
#GB_close { #GB_close {
padding: 2px 5px 0 0; padding: 2px 5px 0 0;
float: right; float: right;
color: #fff;
font-size: 11px; font-size: 11px;
} }

View File

@ -1,7 +1,3 @@
.ComplexTableField_popup {
background: #fff;
}
html { html {
overflow-y: auto !important; overflow-y: auto !important;
} }

View File

@ -1,3 +0,0 @@
#i18nStatus {
margin-left: 0;
}

View File

@ -4,7 +4,6 @@ table.TableListField,
table.CMSList { table.CMSList {
border-collapse: collapse; border-collapse: collapse;
border-spacing: 0; border-spacing: 0;
border : 1px solid #aaaaaa;
width : 100%; width : 100%;
} }
@ -33,12 +32,6 @@ table.CMSList th {
table.TableField thead th, table.TableField thead th,
.TableListField table.data thead th, .TableListField table.data thead th,
table.CMSList thead th { table.CMSList thead th {
background-image: url(../images/thead.png);
background-repeat: repeat-x;
background-position: left bottom;
background-color: #ebeadb;
border-right: 1px solid #aca899;
border-left: 1px solid #ffffff;
white-space: nowrap; white-space: nowrap;
padding: 3px; padding: 3px;
font-size: 12px; font-size: 12px;
@ -50,31 +43,18 @@ table.TableField thead th span,
font-size: 12px; font-size: 12px;
} }
table.TableField thead th a,
.TableListField table.data thead th a,
table.CMSList thead th a {
color: #000;
}
table.TableField thead th span.sortLink, table.TableField thead th span.sortLink,
.TableListField table.data thead th span.sortLink, .TableListField table.data thead th span.sortLink,
table.CMSList thead th span.sortLink { table.CMSList thead th span.sortLink {
overflow: hidden; overflow: hidden;
} }
table.TableField tfoot tr.summary td,
.TableListField table.data tfoot tr.summary td,
table.CMSList tfoot tr.summary td {
background-color : #ebeadb;
}
table.TableField tbody td, table.TableField tbody td,
table.TableField tfoot td, table.TableField tfoot td,
.TableListField table.data tbody td, .TableListField table.data tbody td,
.TableListField table.data tfoot td, .TableListField table.data tfoot td,
table.CMSList tbody td, table.CMSList tbody td,
table.CMSList tfoot td { table.CMSList tfoot td {
border: 1px solid #f1efe2;
padding: 2px 4px; padding: 2px 4px;
} }
@ -103,27 +83,12 @@ table.data tbody td textarea {
table.TableField tbody td.checkbox, table.TableField tbody td.checkbox,
.TableListField table.data tbody td.checkbox, .TableListField table.data tbody td.checkbox,
table.CMSList tbody td.checkbox { table.CMSList tbody td.checkbox {
border : 1px solid #f1efe2;
padding-left : 5px; padding-left : 5px;
background-image : url(../images/checkbox.png); background-image : url(../images/checkbox.png);
background-repeat : repeat-x; background-repeat : repeat-x;
background-position : left bottom; background-position : left bottom;
} }
table.TableField tbody tr.over td,
.TableListField table.data tbody tr.over td,
.TableListField table.data tbody tr.over td input,
table.CMSList tbody td.over td{
background-color: #FFFFBB;
}
table.TableField tbody tr.current td,
.TableListField table.data tbody tr.current td,
table.CMSList tbody td.current td {
background-color: #316ac5;
color : white;
}
.TableListField table.data tfoot .addlink img { .TableListField table.data tfoot .addlink img {
vertical-align: middle; vertical-align: middle;
margin: 3px 6px 3px 3px; margin: 3px 6px 3px 3px;
@ -168,8 +133,6 @@ table.CMSList tbody td.current td {
} }
.TableListField div.utility a { .TableListField div.utility a {
text-decoration: none; text-decoration: none;
border-color: #ccc #999 #999 #ccc;
border-style: double;
color: #333; color: #333;
cursor: pointer; cursor: pointer;
font-size: 11px; font-size: 11px;
@ -178,9 +141,6 @@ table.CMSList tbody td.current td {
padding: 3px 2px; padding: 3px 2px;
width: auto; width: auto;
} }
.TableListField div.utility a:hover {
background: #fff;
}
form .TableField .message { form .TableField .message {
width: auto; width: auto;
@ -203,8 +163,6 @@ form .TableField .message {
text-align:center; text-align:center;
display:block; display:block;
margin-bottom: 5px; margin-bottom: 5px;
background:#ebeadb;
border: 1px #cbc7b7 solid;
position: relative; position: relative;
} }
.TableListField .PageControls * { .TableListField .PageControls * {
@ -240,7 +198,6 @@ form .TableField .message {
#Pagination span { #Pagination span {
display: inline; display: inline;
color: red;
font-size: 14px; font-size: 14px;
} }
#Pagination div { #Pagination div {

View File

@ -0,0 +1,57 @@
# How to extend the CMS interface #
## Introduction ##
The CMS interface works just like any other part of your website: It consists of PHP controllers,
templates, CSS stylesheets and JavaScript. Because it uses the same base elements,
it is relatively easy to extend.
The CMS is built on principles of "[unobtrusive JavaScript](http://en.wikipedia.org/wiki/Unobtrusive_JavaScript)".
## Add a panel to the CMS interface ##
The CMS uses a JavaScript layout manager called [jLayout](http://www.bramstein.com/projects/jlayout/),
which allows us to build complex layouts with minimal JavaScript configuration.
As an example, we're going to add a permanent "quicklinks" bar to popular pages at the bottom
of the "Pages" area (but leave it out of other areas like "Users").
CMS templates are inherited based on their controllers, similiar to subclasses of
the common `Page` object (a new PHP class `MyPage` will look for a `MyPage.ss` template).
We can use this fact to target one specific area only: the `CMSPageEditController` class.
1. Create a new template in `mysite/templates/CMSPageEditController.ss`
2. Copy the template markup of the base implementation at `sapphire/admin/templates/LeftAndMain.ss` into the file.
It will automatically be picked up by the CMS logic.
3. Add a new section after the `$Content` tag:
:::ss
...
<div class="cms-container" data-layout="{type: 'border'}">
$Menu
$Content
<div class="cms-bottom-bar south">
<ul>
<li><a href="admin/page/edit/show/1">Edit "My popular page"</a></li>
<li><a href="admin/page/edit/show/99">Edit "My other page"</a></li>
</ul>
</div>
</div>
...
4. Create a new `mysite/css/CMSPageEditController.css` file and paste the following content:
:::css
.cms-bottom-bar {height: 20px;}
5. Load the new CSS file into the CMS, by adding the following line to `mysite/_config.php`:
:::php
LeftAndMain::require_css('mysite/css/CMSPageEditController.css');
Done! You might have noticed that we didn't write any JavaScript to add our layout manager.
The important piece of information is the `south` class in our new `<div>` structure,
plus the height value in our CSS. It instructs the existing parent layout how to render the element.
The page list itself is hardcoded for now, we'll leave it to the reader to make
this a dynamic and prettier list. Hint: Take a look at the [LeftAndMain](../reference/leftandmain) documentation to find
out how to subclass and replace an admin interface with your own PHP code.

View File

@ -37,26 +37,17 @@ The PHP file defining your new subclass is the first step in the process. This
class MyAdmin extends LeftAndMain { class MyAdmin extends LeftAndMain {
static $url_segment = 'myadmin'; static $url_segment = 'myadmin';
static $url_rule = '$Action/$ID'; static $url_rule = '$Action/$ID';
static $menu_title = 'My Admin'; static $menu_title = 'My Admin';
static $menu_priority = 60; static $menu_priority = 60;
/**
* Initialisation method called before accessing any functionality that BulkLoaderAdmin has to offer
*/
public function init() { public function init() {
Requirements::javascript('cms/javascript/MyAdmin.js'); Requirements::javascript('cms/javascript/MyAdmin.js');
parent::init(); parent::init();
} }
/** /**
* Form which will be shown when we open one of the items
* Form that will be shown when we open one of the items
*/ */
public function getEditForm($id = null) { public function getEditForm($id = null) {
return new Form($this, "EditForm", return new Form($this, "EditForm",
@ -172,7 +163,6 @@ You could insert this code using Requirements from a custom page class.
See [Javascript in the CMS](/topics/javascript#javascript-cms) See [Javascript in the CMS](/topics/javascript#javascript-cms)
## Related ## Related
* `[api:CMSMain]` * `[api:CMSMain]`

View File

@ -97,7 +97,7 @@ This example sets another CSS class typographybis:
... ...
$htmleditor = new HTMLEditorField("ContentBis", "Content Bis"); $htmleditor = new HTMLEditorField("ContentBis", "Content Bis");
$htmleditor->setCSSClass('typographybis'); $htmleditor->setCSSClass('typographybis');
$fields->addFieldToTab("Root.Content.Main", $htmleditor); $fields->addFieldToTab("Root.Content", $htmleditor);
... ...
return $fields; return $fields;
} }

View File

@ -86,7 +86,16 @@ file. Then with this file you can define styles for the CMS or just import the s
See [typography](/reference/typography) for more information. See [typography](/reference/typography) for more information.
## Best Practices ## ## Writing good stylesheets for the CMS ##
* Use the `id` attribute sparingly. Remember that it "closes off" the structure to code reuse, as HTML elements
require unique `id` attributes. Code reuse can happen both in CSS and JavaScript behaviour.
* Separate presentation from structure in class names, e.g. `left-menu` is encoding the component position
(which might change later on). A more structural name could be `cms-menu` (or `cms-tools-menu` for a more specific version)
* Class naming: Use the `cms-` class prefix for major components in the cms interface,
and the `ss-ui-` prefix for extensions to jQuery UI. Don't use the `ui-` class prefix, its reserved for jQuery UI built-in styles.
* Use jQuery UI's built-in styles where possible, e.g. `ui-widget` for a generic container, or `ui-state-highlight`
to highlight a specific component. See the [jQuery UI Theming API](http://jqueryui.com/docs/Theming/API) for a full list.
## Related ## ## Related ##

View File

@ -1,65 +0,0 @@
# Extending the CMS
CMSMain is part of the CMS. It is the controller for the content editor.
## Creating another hierarchical editor by subclassing CMSMain
Sometimes you'll want to provide an administration interface that is pretty much exactly what CMSMain provides, but it's
not appropriate to include your data in with the site content.
Here's how you can do this:
## Using classes other than SiteTree in the site tree
It is possible to use to different classes in two separate site trees. For example, there is the usual site
content tree and a category tree. To change that find:
:::php
static $tree_class = 'SiteTree';
And change the string to the name of the class that will be the base class for classes visible in the site tree.
## Overloading page urls
If using a url other than admin/ for your section then you will need to change the SiteTreeHandlers to use the correct
controller.
Create the init method:
:::php
function init() {
parent::init();
Requirements::javascript('project-name/javascript/Classname_left.js');
}
Where project-name and Classname are changed as appropriate.
Create the javascript file and add the handlers:
:::php
if(typeof SiteTreeHandlers == 'undefined') SiteTreeHandlers = {};
SiteTreeHandlers.parentChanged_url = 'url/ajaxupdateparent';
SiteTreeHandlers.orderChanged_url = 'url/ajaxupdatesort';
SiteTreeHandlers.showRecord_url = 'url/show/';
SiteTreeHandlers.loadPage_url = 'url/show/';
SiteTreeHandlers.loadTree_url = 'url/getsubtree';
where url is the relative link to the page (eg 'admin/categories'). You can change the handler functions as necessary.
## Overloading EditForm
You may need to overload EditForm if your class does not use the `[api:Versioned]` extension.
## Overloading SiteTreeAsUL
The tree hints can sometimes cause problems when reorganising the tree, and the CMSMain::SiteTreeAsUL function uses
`[api:SiteTree]` explicitly. Use:
:::php
public function SiteTreeAsUL() {
// $this->generateDataTreeHints();
$this->generateTreeStylingJS();
return $this->getSiteTreeFor( $this->stat('tree_class') );
}

View File

@ -267,7 +267,7 @@ If you want to remove certain fields from your subclass:
Adds a new text field called FavouriteColour next to the Content field in the CMS Adds a new text field called FavouriteColour next to the Content field in the CMS
:::php :::php
$fields->addFieldToTab('Root.Content.Main', new TextField('FavouriteColour'), 'Content'); $fields->addFieldToTab('Root.Content', new TextField('FavouriteColour'), 'Content');

View File

@ -602,7 +602,7 @@ The CMS has a number of Observer-pattern hooks you can access: (The elements whi
* BeforeSave -- after user clicks 'Save', before AJAX save-request (#Form_EditForm) * BeforeSave -- after user clicks 'Save', before AJAX save-request (#Form_EditForm)
* PageLoaded -- after new SiteTree page is loaded. (#Form_EditForm) * PageLoaded -- after new SiteTree page is loaded. (#Form_EditForm)
* PageSaved -- after AJAX save-request is successful (#Form_EditForm) * PageSaved -- after AJAX save-request is successful (#Form_EditForm)
* SelectionChanged -- when new item is chosen from SiteTree (#sitetree) * SelectionChanged -- when new item is chosen from SiteTree (.cms-tree)
Here's an example of hooking the 'PageLoaded' and 'BeforeSave' methods: Here's an example of hooking the 'PageLoaded' and 'BeforeSave' methods:

View File

@ -86,7 +86,7 @@ want the MenuTitle field to show on your page, which is inherited from `[api:Sit
function getCMSFields() { function getCMSFields() {
$fields = parent::getCMSFields(); $fields = parent::getCMSFields();
$fields->removeFieldFromTab('Root.Content.Main', 'MenuTitle'); $fields->removeFieldFromTab('Root.Content', 'MenuTitle');
return $fields; return $fields;
} }

View File

@ -297,7 +297,7 @@ Page class). One way to fix this is to comment out line 30 in BlogHolder.php and
....... .......
function getCMSFields() { function getCMSFields() {
$fields = parent::getCMSFields(); $fields = parent::getCMSFields();
$fields->removeFieldFromTab("Root.Content.Main","Content"); $fields->removeFieldFromTab("Root.Content","Content");
// $fields->addFieldToTab("Root.Content.Widgets", new WidgetAreaEditor("SideBar")); COMMENT OUT // $fields->addFieldToTab("Root.Content.Widgets", new WidgetAreaEditor("SideBar")); COMMENT OUT
........ ........

View File

@ -174,8 +174,8 @@ method to the *ArticlePage* class.
function getCMSFields() { function getCMSFields() {
$fields = parent::getCMSFields(); $fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Content.Main', new DateField('Date'), 'Content'); $fields->addFieldToTab('Root.Content', new DateField('Date'), 'Content');
$fields->addFieldToTab('Root.Content.Main', new TextField('Author'), 'Content'); $fields->addFieldToTab('Root.Content', new TextField('Author'), 'Content');
return $fields; return $fields;
} }
@ -195,12 +195,12 @@ Firstly, we get the fields from the parent class; we want to add fields, not rep
returned is a `[api:FieldSet]` object. returned is a `[api:FieldSet]` object.
:::php :::php
$fields->addFieldToTab('Root.Content.Main', new DateField('Date'), 'Content'); $fields->addFieldToTab('Root.Content', new DateField('Date'), 'Content');
$fields->addFieldToTab('Root.Content.Main', new TextField('Author'), 'Content'); $fields->addFieldToTab('Root.Content', new TextField('Author'), 'Content');
We can then add our new fields with *addFieldToTab*. The first argument is the tab on which we want to add the field to: We can then add our new fields with *addFieldToTab*. The first argument is the tab on which we want to add the field to:
"Root.Content.Main" is the tab which the content editor is on. The second argument is the field to add; this is not a "Root.Content" is the tab which the content editor is on. The second argument is the field to add; this is not a
database field, but a `[api:FormField]` documentation for more details. database field, but a `[api:FormField]` documentation for more details.
:::php :::php
@ -232,11 +232,11 @@ To make the date field a bit more user friendly, you can add a dropdown calendar
function getCMSFields() { function getCMSFields() {
$fields = parent::getCMSFields(); $fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Content.Main', $dateField = new DateField('Date','Article Date (for example: 20/12/2010)'), 'Content'); $fields->addFieldToTab('Root.Content', $dateField = new DateField('Date','Article Date (for example: 20/12/2010)'), 'Content');
$dateField->setConfig('showcalendar', true); $dateField->setConfig('showcalendar', true);
$dateField->setConfig('dateformat', 'dd/MM/YYYY'); $dateField->setConfig('dateformat', 'dd/MM/YYYY');
$fields->addFieldToTab('Root.Content.Main', new TextField('Author','Author Name'), 'Content'); $fields->addFieldToTab('Root.Content', new TextField('Author','Author Name'), 'Content');
return $fields; return $fields;
} }
@ -244,7 +244,7 @@ To make the date field a bit more user friendly, you can add a dropdown calendar
Let's walk through these changes. Let's walk through these changes.
:::php :::php
$fields->addFieldToTab('Root.Content.Main', $dateField = new DateField('Date','Article Date (for example: 20/12/2010)'), 'Content'); $fields->addFieldToTab('Root.Content', $dateField = new DateField('Date','Article Date (for example: 20/12/2010)'), 'Content');
*$dateField* is added only to the DateField in order to change the configuration. *$dateField* is added only to the DateField in order to change the configuration.
@ -259,7 +259,7 @@ Set *showCalendar* to true to have a calendar appear underneath the Date field w
*dateFormat* allows you to specify how you wish the date to be entered and displayed in the CMS field. *dateFormat* allows you to specify how you wish the date to be entered and displayed in the CMS field.
:::php :::php
$fields->addFieldToTab('Root.Content.Main', new TextField('Author','Author Name'), 'Content'); $fields->addFieldToTab('Root.Content', new TextField('Author','Author Name'), 'Content');
By default the first argument *'Date'* or *'Author'* is shown as the title, however this might not be that helpful so to change the title, By default the first argument *'Date'* or *'Author'* is shown as the title, however this might not be that helpful so to change the title,
add the new title as the second argument. See the `[api:DateField]` documentation for more details. add the new title as the second argument. See the `[api:DateField]` documentation for more details.

View File

@ -135,7 +135,7 @@ The second step is to add the table in the method *getCMSFields* which will allo
); );
$tablefield->setParentClass('Project'); $tablefield->setParentClass('Project');
$fields->addFieldToTab( 'Root.Content.Student', $tablefield ); $fields->addFieldToTab( 'Root.Student', $tablefield );
return $fields; return $fields;
} }
@ -259,9 +259,9 @@ The first step is to create the mentor object and set the relation with the *Stu
function getCMSFields() { function getCMSFields() {
$fields = parent::getCMSFields(); $fields = parent::getCMSFields();
$fields->addFieldToTab( 'Root.Content.Main', new TextField( 'FirstName' ) ); $fields->addFieldToTab( 'Root.Content', new TextField( 'FirstName' ) );
$fields->addFieldToTab( 'Root.Content.Main', new TextField( 'Lastname' ) ); $fields->addFieldToTab( 'Root.Content', new TextField( 'Lastname' ) );
$fields->addFieldToTab( 'Root.Content.Main', new TextField( 'Nationality' ) ); $fields->addFieldToTab( 'Root.Content', new TextField( 'Nationality' ) );
return $fields; return $fields;
} }
@ -313,7 +313,7 @@ The second step is to add the table in the method *getCMSFields* which will allo
); );
$tablefield->setAddTitle( 'A Student' ); $tablefield->setAddTitle( 'A Student' );
$fields->addFieldToTab( 'Root.Content.Students', $tablefield ); $fields->addFieldToTab( 'Root.Students', $tablefield );
return $fields; return $fields;
} }
@ -429,7 +429,7 @@ relation.
); );
$modulesTablefield->setAddTitle( 'A Module' ); $modulesTablefield->setAddTitle( 'A Module' );
$fields->addFieldToTab( 'Root.Content.Modules', $modulesTablefield ); $fields->addFieldToTab( 'Root.Modules', $modulesTablefield );
return $fields; return $fields;
} }

View File

@ -17,7 +17,7 @@
* if ($galleries) { * if ($galleries) {
* $galleries = $galleries->toDropdownMap('ID', 'Title', '(Select one)', true); * $galleries = $galleries->toDropdownMap('ID', 'Title', '(Select one)', true);
* } * }
* $fields->addFieldToTab('Root.Content.Main', new DropdownField('GalleryID', 'Gallery', $galleries), 'Content'); * $fields->addFieldToTab('Root.Content', new DropdownField('GalleryID', 'Gallery', $galleries), 'Content');
* </code> * </code>
* *
* As you see, you need to put "GalleryID", rather than "Gallery" here. * As you see, you need to put "GalleryID", rather than "Gallery" here.

View File

@ -28,7 +28,7 @@
* $fields = parent::getCMSFields(); * $fields = parent::getCMSFields();
* *
* $fields->addFieldToTab( * $fields->addFieldToTab(
* 'Root.Content.Main', * 'Root.Content',
* new FieldGroup( * new FieldGroup(
* new TimeField("StartTime","What's the start time?"), * new TimeField("StartTime","What's the start time?"),
* new TimeField("EndTime","What's the end time?") * new TimeField("EndTime","What's the end time?")

View File

@ -10,6 +10,8 @@
*/ */
class FileIFrameField extends FileField { class FileIFrameField extends FileField {
protected $template = 'FileIFrameField';
public static $allowed_actions = array ( public static $allowed_actions = array (
'iframe', 'iframe',
'EditFileForm', 'EditFileForm',
@ -49,7 +51,7 @@ class FileIFrameField extends FileField {
* @return string * @return string
*/ */
public function Field() { public function Field() {
Requirements::css(SAPPHIRE_DIR . '/thirdparty/jquery-ui-themes/smoothness/jquery.ui.all.css'); Requirements::css(SAPPHIRE_DIR . '/thirdparty/jquery-ui-themes/smoothness/jquery-ui.css');
Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang'); Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery/jquery.js'); Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery/jquery.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/jquery-ui.js'); Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/jquery-ui.js');
@ -109,7 +111,7 @@ class FileIFrameField extends FileField {
Requirements::css('sapphire/css/FileIFrameField.css'); Requirements::css('sapphire/css/FileIFrameField.css');
return $this->renderWith('FileIFrameField'); return $this->renderWith($this->template);
} }
/** /**

View File

@ -1101,9 +1101,9 @@ class Form extends RequestHandler {
* than <% control FormObject %> * than <% control FormObject %>
*/ */
function forTemplate() { function forTemplate() {
return $this->renderWith(array( return $this->renderWith(array_merge(
$this->getTemplate(), (array)$this->getTemplate(),
'Form' array('Form')
)); ));
} }

View File

@ -68,6 +68,11 @@ class FormField extends RequestHandler {
*/ */
protected $disabled = false; protected $disabled = false;
/**
* @var String
*/
protected $template;
/** /**
* @var Custom Validation Message for the Field * @var Custom Validation Message for the Field
*/ */
@ -356,6 +361,22 @@ class FormField extends RequestHandler {
return $this->customValidationMessage; return $this->customValidationMessage;
} }
/**
* Set name of template (without path or extension)
*
* @param String
*/
function setTemplate($template) {
$this->template = $template;
}
/**
* @return String
*/
function getTemplate() {
return $this->template;
}
/** /**
* Returns the form field - used by templates. * Returns the form field - used by templates.
* Although FieldHolder is generally what is inserted into templates, all of the field holder * Although FieldHolder is generally what is inserted into templates, all of the field holder

View File

@ -275,6 +275,7 @@ class HtmlEditorField_Toolbar extends RequestHandler {
$form->unsetValidator(); $form->unsetValidator();
$form->loadDataFrom($this); $form->loadDataFrom($this);
$form->addExtraClass('htmleditorfield-form htmleditorfield-linkform');
$this->extend('updateLinkForm', $form); $this->extend('updateLinkForm', $form);
@ -347,6 +348,7 @@ class HtmlEditorField_Toolbar extends RequestHandler {
$form->unsetValidator(); $form->unsetValidator();
$form->disableSecurityToken(); $form->disableSecurityToken();
$form->loadDataFrom($this); $form->loadDataFrom($this);
$form->addExtraClass('htmleditorfield-form htmleditorfield-imageform');
return $form; return $form;
} }
@ -385,6 +387,7 @@ class HtmlEditorField_Toolbar extends RequestHandler {
$form->unsetValidator(); $form->unsetValidator();
$form->loadDataFrom($this); $form->loadDataFrom($this);
$form->disableSecurityToken(); $form->disableSecurityToken();
$form->addExtraClass('htmleditorfield-form htmleditorfield-flashform');
return $form; return $form;
} }

View File

@ -7,6 +7,9 @@
* @subpackage fields-structural * @subpackage fields-structural
*/ */
class SelectionGroup extends CompositeField { class SelectionGroup extends CompositeField {
protected $template = "SelectionGroup";
/** /**
* Create a new selection group. * Create a new selection group.
* @param name The field name of the selection group. * @param name The field name of the selection group.
@ -83,7 +86,7 @@ class SelectionGroup extends CompositeField {
Requirements::javascript(SAPPHIRE_DIR . '/javascript/SelectionGroup.js'); Requirements::javascript(SAPPHIRE_DIR . '/javascript/SelectionGroup.js');
Requirements::css(SAPPHIRE_DIR . '/css/SelectionGroup.css'); Requirements::css(SAPPHIRE_DIR . '/css/SelectionGroup.css');
return $this->renderWith("SelectionGroup"); return $this->renderWith($this->template);
} }
} }

View File

@ -27,6 +27,8 @@
*/ */
class TabSet extends CompositeField { class TabSet extends CompositeField {
protected $template = "TabSetFieldHolder";
/** /**
* @param string $name Identifier * @param string $name Identifier
* @param string $title (Optional) Natural language title of the tabset * @param string $title (Optional) Natural language title of the tabset
@ -73,14 +75,13 @@ class TabSet extends CompositeField {
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/jquery-ui.js'); Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/jquery-ui.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-cookie/jquery.cookie.js'); Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-cookie/jquery.cookie.js');
Requirements::css(SAPPHIRE_DIR . '/thirdparty/jquery-ui-themes/smoothness/jquery.ui.all.css'); Requirements::css(SAPPHIRE_DIR . '/thirdparty/jquery-ui-themes/smoothness/jquery.ui.css');
Requirements::css(SAPPHIRE_DIR . '/thirdparty/jquery-ui-themes/smoothness/jquery.ui.tabs.css');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-entwine/dist/jquery.entwine-dist.js'); Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-entwine/dist/jquery.entwine-dist.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/TabSet.js'); Requirements::javascript(SAPPHIRE_DIR . '/javascript/TabSet.js');
return $this->renderWith("TabSetFieldHolder"); return $this->renderWith($this->template);
} }
/** /**

View File

@ -480,10 +480,9 @@ class TableField extends TableListField {
* Sets the template to be rendered with * Sets the template to be rendered with
*/ */
function FieldHolder() { function FieldHolder() {
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/prototype/prototype.js'); Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery/jquery.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/behaviour/behaviour.js'); Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/behaviour/behaviour.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/prototype_improvements.js'); Requirements::javascript(SAPPHIRE_DIR . '/javascript/prototype_improvements.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/scriptaculous/effects.js');
Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang'); Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/TableListField.js'); Requirements::javascript(SAPPHIRE_DIR . '/javascript/TableListField.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/TableField.js'); Requirements::javascript(SAPPHIRE_DIR . '/javascript/TableField.js');

View File

@ -295,10 +295,10 @@ class TableListField extends FormField {
} }
function FieldHolder() { function FieldHolder() {
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery/jquery.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/prototype/prototype.js'); Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/prototype/prototype.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/behaviour/behaviour.js'); Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/behaviour/behaviour.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/prototype_improvements.js'); Requirements::javascript(SAPPHIRE_DIR . '/javascript/prototype_improvements.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/scriptaculous/effects.js');
Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang'); Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/TableListField.js'); Requirements::javascript(SAPPHIRE_DIR . '/javascript/TableListField.js');
Requirements::css(SAPPHIRE_DIR . '/css/TableListField.css'); Requirements::css(SAPPHIRE_DIR . '/css/TableListField.css');

View File

@ -6,6 +6,8 @@
*/ */
class ToggleCompositeField extends CompositeField { class ToggleCompositeField extends CompositeField {
protected $template = "ToggleCompositeField";
/** /**
* @var $headingLevel int * @var $headingLevel int
*/ */
@ -26,7 +28,7 @@ class ToggleCompositeField extends CompositeField {
Requirements::javascript(SAPPHIRE_DIR . "/javascript/prototype_improvements.js"); Requirements::javascript(SAPPHIRE_DIR . "/javascript/prototype_improvements.js");
Requirements::javascript(SAPPHIRE_DIR . "/javascript/ToggleCompositeField.js"); Requirements::javascript(SAPPHIRE_DIR . "/javascript/ToggleCompositeField.js");
return $this->renderWith("ToggleCompositeField"); return $this->renderWith($this->template);
} }
/** /**

View File

@ -121,7 +121,7 @@ class TreeDropdownField extends FormField {
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jstree/jquery.jstree.js'); Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jstree/jquery.jstree.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/TreeDropdownField.js'); Requirements::javascript(SAPPHIRE_DIR . '/javascript/TreeDropdownField.js');
Requirements::css(SAPPHIRE_DIR . '/thirdparty/jquery-ui-themes/smoothness/jquery.ui.all.css'); Requirements::css(SAPPHIRE_DIR . '/thirdparty/jquery-ui-themes/smoothness/jquery-ui.css');
Requirements::css(SAPPHIRE_DIR . '/css/TreeDropdownField.css'); Requirements::css(SAPPHIRE_DIR . '/css/TreeDropdownField.css');
if($this->Value() && $record = $this->objectForKey($this->Value())) { if($this->Value() && $record = $this->objectForKey($this->Value())) {

View File

@ -87,7 +87,7 @@ class TreeMultiselectField extends TreeDropdownField {
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jstree/jquery.jstree.js'); Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jstree/jquery.jstree.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/TreeDropdownField.js'); Requirements::javascript(SAPPHIRE_DIR . '/javascript/TreeDropdownField.js');
Requirements::css(SAPPHIRE_DIR . '/thirdparty/jquery-ui-themes/smoothness/jquery.ui.all.css'); Requirements::css(SAPPHIRE_DIR . '/thirdparty/jquery-ui-themes/smoothness/jquery-ui.css');
Requirements::css(SAPPHIRE_DIR . '/css/TreeDropdownField.css'); Requirements::css(SAPPHIRE_DIR . '/css/TreeDropdownField.css');
$value = ''; $value = '';

BIN
images/add.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

View File

@ -1,6 +1,6 @@
(function($) { (function($) {
$('.field.date input.text').live('click', function() { $('.field.date input.text').live('click', function() {
var holder = $(this).parents('.field.date:first'), config = holder.metadata(); var holder = $(this).parents('.field.date:first'), config = holder.metadata({type: 'class'});
if(!config.showcalendar) return; if(!config.showcalendar) return;
if(config.locale && $.datepicker.regional[config.locale]) { if(config.locale && $.datepicker.regional[config.locale]) {

View File

@ -7,7 +7,24 @@
*/ */
(function($) { (function($) {
$(document).ready(function() { $(document).ready(function() {
// Move title from headline to (jQuery compatible) title attribute
$('.htmleditorfield-form').each(function() {
var titleEl = $(this).find(':header:first');
$(this).attr('title', titleEl.text());
titleEl.remove();
});
$('.htmleditorfield-form').dialog({
autoOpen: false,
bgiframe: true,
modal: true,
height: 500,
width: 500,
ghost: true
});
/** /**
* On page refresh load the initial images (in root) * On page refresh load the initial images (in root)
*/ */
@ -32,7 +49,7 @@
* On folder change - lookup the new images * On folder change - lookup the new images
*/ */
$("#Form_EditorToolbarImageForm_Files-0").change(function() { $("#Form_EditorToolbarImageForm_Files-0").change(function() {
$("#contentPanel #Form_EditorToolbarImageForm").ajaxForm({ $(".cms-editor-dialogs #Form_EditorToolbarImageForm").ajaxForm({
url: 'admin/assets/UploadForm?action_doUpload=1', url: 'admin/assets/UploadForm?action_doUpload=1',
iframe: true, iframe: true,
dataType: 'json', dataType: 'json',

View File

@ -4,19 +4,6 @@
* *
* TODO relies on include-order at the moment to override actions :/ * TODO relies on include-order at the moment to override actions :/
*/ */
Effect.FadeOut = function(element,callback) {
element = $(element);
var oldOpacity = Element.getInlineOpacity(element);
var options = Object.extend({
from: Element.getOpacity(element) || 1.0,
to: 0.0,
afterFinishInternal: function(effect) {
effect.element.parentNode.removeChild(effect.element);
}
}, arguments[1] || {});
return new Effect.Opacity(element,options);
}
TableField = Class.create(); TableField = Class.create();
TableField.prototype = { TableField.prototype = {
@ -61,7 +48,7 @@ TableField.prototype = {
|| params["childID"] <= 0 || (recordID <= 0 || recordID == false) || params["childID"] <= 0 || (recordID <= 0 || recordID == false)
){ ){
if( row.parentNode.getElementsByTagName('tr').length > 1 ) { if( row.parentNode.getElementsByTagName('tr').length > 1 ) {
Effect.FadeOut(row); jQuery(row).fadeOut();
} else { } else {
// clear all fields in the row // clear all fields in the row
var fields = row.getElementsByTagName('input'); var fields = row.getElementsByTagName('input');
@ -83,20 +70,15 @@ TableField.prototype = {
'method': 'post', 'method': 'post',
'data': {ajax: 1, 'SecurityID': $('SecurityID') ? $('SecurityID').value : null}, 'data': {ajax: 1, 'SecurityID': $('SecurityID') ? $('SecurityID').value : null},
'success': function(response){ 'success': function(response){
Effect.Fade( jQuery(row).fadeOut('fast', function() {
row,
{
afterFinish: function(obj) {
// remove row from DOM // remove row from DOM
obj.element.parentNode.removeChild(obj.element); this.element.parentNode.removeChild(obj.element);
// recalculate summary if needed (assumes that TableListField.js is present) // recalculate summary if needed (assumes that TableListField.js is present)
// TODO Proper inheritance // TODO Proper inheritance
if(self._summarise) self._summarise(); if(self._summarise) self._summarise();
// custom callback // custom callback
if(self.callback_deleteRecord) self.callback_deleteRecord(e); if(self.callback_deleteRecord) self.callback_deleteRecord(e);
} });
}
);
}, },
'error': ajaxErrorHandler 'error': ajaxErrorHandler
}); });

View File

@ -97,22 +97,17 @@ TableListField.prototype = {
jQuery.ajax({ jQuery.ajax({
'url': link.getAttribute("href"), 'url': link.getAttribute("href"),
'method': 'post', 'method': 'post',
'data': {forceajax: 1, SecurityID: $('SecurityID') ? $('SecurityID').value : null}, 'data': {forceajax: 1, SecurityID: jQuery('input[name=SecurityID]').val()},
'success': function(){ 'success': function(){
Effect.Fade( jQuery(row).fadeOut('fast', function() {
row,
{
afterFinish: function(obj) {
// remove row from DOM // remove row from DOM
obj.element.parentNode.removeChild(obj.element); this.element.parentNode.removeChild(obj.element);
// recalculate summary if needed (assumes that TableListField.js is present) // recalculate summary if needed (assumes that TableListField.js is present)
// TODO Proper inheritance // TODO Proper inheritance
if(self._summarise) self._summarise(); if(self._summarise) self._summarise();
// custom callback // custom callback
if(self.callback_deleteRecord) self.callback_deleteRecord(e); if(self.callback_deleteRecord) self.callback_deleteRecord(e);
} });
}
);
}, },
'error': this.ajaxErrorHandler 'error': this.ajaxErrorHandler
}); });
@ -183,13 +178,12 @@ TableListField.prototype = {
if(el.getAttribute('href')) { if(el.getAttribute('href')) {
jQuery.ajax({ jQuery.ajax({
'url': el.getAttribute('href'), 'url': el.getAttribute('href'),
'data': {'update': 1, 'params': (params)}, 'data': {'update': 1},
'success': function(response) { 'success': function(response) {
Element.replace(self.id, response.responseText); jQuery('#' + self.id).replaceWith(response)
// reapply behaviour and reattach methods to TF container node // reapply behaviour and reattach methods to TF container node
// e.g. <div class="TableListField"> // e.g. <div class="TableListField">
Behaviour.apply($(self.id), true); Behaviour.apply($(self.id), true);
if(oncomplete) oncomplete.apply(response);
} }
}); });
} }

View File

@ -14,6 +14,7 @@ if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
'FILEIFRAMEFIELD.DELETEFILE': 'Delete File', 'FILEIFRAMEFIELD.DELETEFILE': 'Delete File',
'FILEIFRAMEFIELD.UNATTACHFILE': 'Un-Attach File', 'FILEIFRAMEFIELD.UNATTACHFILE': 'Un-Attach File',
'FILEIFRAMEFIELD.DELETEIMAGE': 'Delete Image', 'FILEIFRAMEFIELD.DELETEIMAGE': 'Delete Image',
'FILEIFRAMEFIELD.CONFIRMDELETE': 'Are you sure you want to delete this file?' 'FILEIFRAMEFIELD.CONFIRMDELETE': 'Are you sure you want to delete this file?',
'LeftAndMain.IncompatBrowserWarning': 'Your browser is not compatible with the CMS interface. Please use Internet Explorer 7+, Google Chrome 10+ or Mozilla Firefox 3.5+.'
}); });
} }

View File

@ -1,7 +0,0 @@
Prototype replaces document.getElementsByClassName with it's own version. However many browsers
now come with their own implementation, and prototype's is much slower.
This fixes this by restoring the original after prototype is loaded.
@todo: Safari 3 used to come with a broken document.getElementsByClassName. We should use
the same checks jQuery does and not restore the function if it doesn't work the same as the spec.

View File

@ -1 +0,0 @@
var browserGetElementsByClassName = document.getElementsByClassName;

View File

@ -1 +0,0 @@
if (browserGetElementsByClassName) document.getElementsByClassName = browserGetElementsByClassName;

View File

@ -5,14 +5,10 @@ ToolbarForm.prototype = {
else this.open(ed); else this.open(ed);
}, },
close: function(ed) { close: function(ed) {
if(this.style.display == 'block') { jQuery(this).dialog('close');
this.style.display = 'none';
}
}, },
open: function(ed) { open: function(ed) {
if(this.style.display != 'block') { jQuery(this).dialog('open');
this.style.display = 'block';
}
}, },
onsubmit: function() { onsubmit: function() {
return false; return false;
@ -140,6 +136,7 @@ LinkForm.prototype = {
}, },
updateSelection: function(ed) { updateSelection: function(ed) {
if(ed == null) ed = tinyMCE.activeEditor;
if(ed.selection.getRng()) { if(ed.selection.getRng()) {
this.originalSelection = ed.selection.getRng(); this.originalSelection = ed.selection.getRng();
} }
@ -164,6 +161,7 @@ LinkForm.prototype = {
Form.Element.setValue(this.elements[i], selected); Form.Element.setValue(this.elements[i], selected);
} }
} }
}
// If we haven't selected an existing link, then just make sure we default to "internal" for the link // If we haven't selected an existing link, then just make sure we default to "internal" for the link
// type. // type.
@ -171,7 +169,6 @@ LinkForm.prototype = {
if(!Form.Element.getValue(this.elements.LinkType)) Form.Element.setValue(this.elements.LinkType, 'internal'); if(!Form.Element.getValue(this.elements.LinkType)) Form.Element.setValue(this.elements.LinkType, 'internal');
} }
this.linkTypeChanged(data ? false : true); this.linkTypeChanged(data ? false : true);
}
}, },
handleaction_insert: function() { handleaction_insert: function() {
@ -437,6 +434,7 @@ SideFormAction.prototype = {
} catch(er) { } catch(er) {
alert("An error occurred. Please try again, or reload the CMS if the problem persists.\n\nError details: " + er.message); alert("An error occurred. Please try again, or reload the CMS if the problem persists.\n\nError details: " + er.message);
} }
jQuery(this).parents('form').dialog('close');
} else { } else {
alert("Couldn't find form method handle" + this.name); alert("Couldn't find form method handle" + this.name);
} }
@ -564,13 +562,8 @@ ImageForm.prototype = {
if(this.selectedImage) { if(this.selectedImage) {
this.selectedImage.insert(); this.selectedImage.insert();
} }
}, }
handleaction_editimage: function() {
if(this.selectedImage) {
this.selectedImage.edit();
}
}
} }
ImageThumbnail = Class.create(); ImageThumbnail = Class.create();
@ -584,42 +577,6 @@ ImageThumbnail.prototype = {
return false; return false;
}, },
edit: function() {
var windowWidth = Element.getDimensions(window.top.document.body).width;
var windowHeight = Element.getDimensions(window.top.document.body).height;
var iframe = window.top.document.getElementById('imageEditorIframe');
if(iframe != null) {
iframe.parentNode.removeChild(iframe);
}
iframe = window.top.document.createElement('iframe');
var fileToEdit = this.href;
iframe.setAttribute("src","admin/ImageEditor?fileToEdit=" + fileToEdit);
iframe.id = 'imageEditorIframe';
iframe.style.width = windowWidth - 6 + 'px';
iframe.style.height = windowHeight + 10 + 'px';
iframe.style.zIndex = "1000";
iframe.style.position = "absolute";
iframe.style.top = "8px";
iframe.style.left = "8px";
window.top.document.body.appendChild(iframe);
var divLeft = window.top.document.createElement('div');
var divRight = window.top.document.createElement('div');
divLeft.style.width = "8px";
divLeft.style.height = "300%";
divLeft.style.zIndex = "1000";
divLeft.style.top = "0";
divLeft.style.position = "absolute";
divRight.style.width = "10px";
divRight.style.height = "300%";
divRight.style.zIndex = "1000";
divRight.style.top = "0";
divRight.style.position = "absolute";
divRight.style.left = Element.getDimensions(divLeft).width + Element.getDimensions(iframe).width - 4 + 'px';
window.top.document.body.appendChild(divLeft);
window.top.document.body.appendChild(divRight);
return;
},
insert: function() { insert: function() {
var formObj = $('Form_EditorToolbarImageForm'); var formObj = $('Form_EditorToolbarImageForm');
var altText = formObj.elements.AltText.value; var altText = formObj.elements.AltText.value;
@ -663,13 +620,6 @@ ImageThumbnail.prototype = {
if(el && el.nodeName == 'IMG') { if(el && el.nodeName == 'IMG') {
ed.dom.setAttribs(el, attributes); ed.dom.setAttribs(el, attributes);
} else { } else {
// Focus gets saved in tinymce_ssbuttons when opening the sidebar.
// Unless the focus has changed in the meantime, reset it to the previous position.
// This is necessary because IE can lose its focus if any of the sidebar input fields are used.
if(ed.ss_focus_bookmark) {
ed.selection.moveToBookmark(ed.ss_focus_bookmark);
delete ed.ss_focus_bookmark;
}
ed.execCommand('mceInsertContent', false, html, { ed.execCommand('mceInsertContent', false, html, {
skip_undo : 1 skip_undo : 1
}); });
@ -702,29 +652,6 @@ function reselectImage(transport) {
this.addToTinyMCE = this.addToTinyMCE.bind(this); this.addToTinyMCE = this.addToTinyMCE.bind(this);
} }
function imageEditorClosed() {
if(self.refreshAsset) {
refreshAsset();
}
if($('Form_EditorToolbarImageForm')) {
if($('Form_EditorToolbarImageForm').style.display != "none") {
// FInd the selected image
links = $('Image').getElementsByTagName('a');
for(i =0; link = links[i]; i++) {
if(link.className == 'selectedImage') {
var quesmark = link.href.lastIndexOf('?');
selectedimage = link.href.substring(0, quesmark);
break;
}
}
// Trick the folder dropdown into registering a change, so the image thumbnails are reloaded
folderID = $('Form_EditorToolbarImageForm_FolderID').value;
$('Image').ajaxGetFiles(folderID, null, reselectImage);
}
}
}
FlashForm = Class.extend('ToolbarForm'); FlashForm = Class.extend('ToolbarForm');
FlashForm.prototype = { FlashForm.prototype = {
initialize: function() { initialize: function() {
@ -962,13 +889,3 @@ function sapphiremce_cleanup(type, value) {
} }
return value; return value;
} }
contentPanelCloseButton = Class.create();
contentPanelCloseButton.prototype = {
onclick: function() {
tinyMCE.activeEditor.execCommand('ssclosesidepanel');
}
}
contentPanelCloseButton.applyTo('#contentPanel h2 img');

View File

@ -1633,17 +1633,8 @@ class Member_ProfileForm extends Form {
function __construct($controller, $name, $member) { function __construct($controller, $name, $member) {
Requirements::clear(); Requirements::clear();
Requirements::css(CMS_DIR . '/css/typography.css'); Requirements::css(CMS_DIR . '/css/typography.css');
Requirements::css(CMS_DIR . '/css/cms_right.css');
Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/prototype/prototype.js");
Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/behaviour/behaviour.js");
Requirements::javascript(SAPPHIRE_DIR . "/javascript/prototype_improvements.js");
Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/scriptaculous/scriptaculous.js");
Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/scriptaculous/controls.js");
Requirements::css(SAPPHIRE_DIR . "/css/Form.css"); Requirements::css(SAPPHIRE_DIR . "/css/Form.css");
Requirements::css(SAPPHIRE_DIR . "/css/MemberProfileForm.css");
$fields = $member->getCMSFields(); $fields = $member->getCMSFields();
$fields->push(new HiddenField('ID','ID',$member->ID)); $fields->push(new HiddenField('ID','ID',$member->ID));

View File

@ -365,10 +365,6 @@ class Security extends Controller {
// only display tabs when more than one authenticator is provided // only display tabs when more than one authenticator is provided
// to save bandwidth and reduce the amount of custom styling needed // to save bandwidth and reduce the amount of custom styling needed
if(count($forms) > 1) { if(count($forms) > 1) {
Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/prototype/prototype.js");
Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/behaviour/behaviour.js");
Requirements::javascript(SAPPHIRE_DIR . "/javascript/prototype_improvements.js");
Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/scriptaculous/effects.js");
Requirements::css(SAPPHIRE_DIR . "/css/Form.css"); Requirements::css(SAPPHIRE_DIR . "/css/Form.css");
// Needed because the <base href=".."> in the template makes problems // Needed because the <base href=".."> in the template makes problems
@ -381,8 +377,7 @@ class Security extends Controller {
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-entwine/dist/jquery.entwine-dist.js'); Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-entwine/dist/jquery.entwine-dist.js');
Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery.ui.all.css'); Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css');
Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery.ui.tabs.css');
Requirements::css(SAPPHIRE_DIR . '/css/Security_login.css'); Requirements::css(SAPPHIRE_DIR . '/css/Security_login.css');
@ -438,11 +433,6 @@ class Security extends Controller {
* @return string Returns the "lost password" page as HTML code. * @return string Returns the "lost password" page as HTML code.
*/ */
public function lostpassword() { public function lostpassword() {
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/prototype/prototype.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/behaviour/behaviour.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/prototype_improvements.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/scriptaculous/effects.js');
if(class_exists('SiteTree')) { if(class_exists('SiteTree')) {
$tmpPage = new Page(); $tmpPage = new Page();
$tmpPage->Title = _t('Security.LOSTPASSWORDHEADER', 'Lost Password'); $tmpPage->Title = _t('Security.LOSTPASSWORDHEADER', 'Lost Password');
@ -501,11 +491,6 @@ class Security extends Controller {
* @return string Returns the "password sent" page as HTML code. * @return string Returns the "password sent" page as HTML code.
*/ */
public function passwordsent($request) { public function passwordsent($request) {
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/behaviour/behaviour.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/prototype/prototype.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/prototype_improvements.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/scriptaculous/effects.js');
if(class_exists('SiteTree')) { if(class_exists('SiteTree')) {
$tmpPage = new Page(); $tmpPage = new Page();
$tmpPage->Title = _t('Security.LOSTPASSWORDHEADER'); $tmpPage->Title = _t('Security.LOSTPASSWORDHEADER');

View File

@ -213,7 +213,7 @@ class FieldSetTest extends SapphireTest {
/* 2 tabs get created within a TabSet inside our set */ /* 2 tabs get created within a TabSet inside our set */
$tab = new TabSet('Root', $tab = new TabSet('Root',
new TabSet('Content', new TabSet('MyContent',
$mainTab = new Tab('Main'), $mainTab = new Tab('Main'),
$otherTab = new Tab('Others') $otherTab = new Tab('Others')
) )
@ -221,8 +221,8 @@ class FieldSetTest extends SapphireTest {
$fields->push($tab); $fields->push($tab);
/* Some fields get added to the 2 tabs we just created */ /* Some fields get added to the 2 tabs we just created */
$fields->addFieldToTab('Root.Content.Main', new TextField('Country')); $fields->addFieldToTab('Root.MyContent.Main', new TextField('Country'));
$fields->addFieldToTab('Root.Content.Others', new TextField('Email')); $fields->addFieldToTab('Root.MyContent.Others', new TextField('Email'));
/* The fields we just added actually exists in the set */ /* The fields we just added actually exists in the set */
$this->assertNotNull($fields->dataFieldByName('Country')); $this->assertNotNull($fields->dataFieldByName('Country'));
@ -236,8 +236,8 @@ class FieldSetTest extends SapphireTest {
$this->assertEquals(1, $mainTab->Fields()->Count()); $this->assertEquals(1, $mainTab->Fields()->Count());
$this->assertEquals(1, $otherTab->Fields()->Count()); $this->assertEquals(1, $otherTab->Fields()->Count());
$this->assertNotNull($fields->fieldByName('Root.Content')); $this->assertNotNull($fields->fieldByName('Root.MyContent'));
$this->assertNotNull($fields->fieldByName('Root.Content.Main')); $this->assertNotNull($fields->fieldByName('Root.MyContent'));
} }
function testTabTitles() { function testTabTitles() {

View File

@ -1,8 +0,0 @@
---
format: 1
handler:
commit: 0dbb5ec89ce8f493e33b83433b03d6feaec867fc
branch: master
lock: false
repository_url: git://github.com/jquery/jquery-metadata.git
repository_class: Piston::Git::Repository

View File

@ -7,7 +7,7 @@
* http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html * http://www.gnu.org/licenses/gpl.html
* *
* Revision: $Id: jquery.metadata.js 4187 2007-12-16 17:15:27Z joern.zaefferer $ * Revision: $Id: jquery.metadata.js 3640 2007-10-11 18:34:38Z pmclanahan $
* *
*/ */
@ -15,7 +15,7 @@
* Sets the type of metadata to use. Metadata is encoded in JSON, and each property * Sets the type of metadata to use. Metadata is encoded in JSON, and each property
* in the JSON will become a property of the element itself. * in the JSON will become a property of the element itself.
* *
* There are three supported types of metadata storage: * There are four supported types of metadata storage:
* *
* attr: Inside an attribute. The name parameter indicates *which* attribute. * attr: Inside an attribute. The name parameter indicates *which* attribute.
* *
@ -23,6 +23,7 @@
* *
* elem: Inside a child element (e.g. a script tag). The * elem: Inside a child element (e.g. a script tag). The
* name parameter indicates *which* element. * name parameter indicates *which* element.
* html5: Values are stored in data-* attributes.
* *
* The metadata for an element is loaded the first time the element is accessed via jQuery. * The metadata for an element is loaded the first time the element is accessed via jQuery.
* *
@ -46,6 +47,11 @@
* @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label" * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
* @desc Reads metadata from a nested script element * @desc Reads metadata from a nested script element
* *
* @example <p id="one" class="some_class" data-item_id="1" data-item_label="Label">This is a p</p>
* @before $.metadata.setType("html5")
* @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
* @desc Reads metadata from a series of data-* attributes
*
* @param String type The encoding type * @param String type The encoding type
* @param String name The name of the attribute to be used to get metadata (optional) * @param String name The name of the attribute to be used to get metadata (optional)
* @cat Plugins/Metadata * @cat Plugins/Metadata
@ -79,13 +85,36 @@ $.extend({
data = "{}"; data = "{}";
var getData = function(data) {
if(typeof data != "string") return data;
if( data.indexOf('{') < 0 ) {
data = eval("(" + data + ")");
}
}
var getObject = function(data) {
if(typeof data != "string") return data;
data = eval("(" + data + ")");
return data;
}
if ( settings.type == "html5" ) {
var object = {};
$( elem.attributes ).each(function() {
var name = this.nodeName;
if(name.match(/^data-/)) name = name.replace(/^data-/, '');
else return true;
object[name] = getObject(this.nodeValue);
});
} else {
if ( settings.type == "class" ) { if ( settings.type == "class" ) {
var m = settings.cre.exec( elem.className ); var m = settings.cre.exec( elem.className );
if ( m ) if ( m )
data = m[1]; data = m[1];
} else if ( settings.type == "elem" ) { } else if ( settings.type == "elem" ) {
if( !elem.getElementsByTagName ) if( !elem.getElementsByTagName ) return;
return undefined;
var e = elem.getElementsByTagName(settings.name); var e = elem.getElementsByTagName(settings.name);
if ( e.length ) if ( e.length )
data = $.trim(e[0].innerHTML); data = $.trim(e[0].innerHTML);
@ -94,14 +123,11 @@ $.extend({
if ( attr ) if ( attr )
data = attr; data = attr;
} }
object = getObject(data.indexOf("{") < 0 ? "{" + data + "}" : data);
}
if ( data.indexOf( '{' ) <0 ) $.data( elem, settings.single, object );
data = "{" + data + "}"; return object;
data = eval("(" + data + ")");
$.data( elem, settings.single, data );
return data;
} }
} }
}); });

View File

@ -57,44 +57,47 @@
return $.vakata.css.get_css(rule_name, true, sheet); return $.vakata.css.get_css(rule_name, true, sheet);
}, },
add_sheet : function(opts) { add_sheet : function(opts) {
var tmp = false, is_new = true; // MODIFIED ischommer/SilverStripe: add_sheet significantly slows down rendering,
if(opts.str) { // we're loading all required CSS directly rather than adding it inline
if(opts.title) { tmp = $("style[id='" + opts.title + "-stylesheet']")[0]; }
if(tmp) { is_new = false; } // var tmp = false, is_new = true;
else { // if(opts.str) {
tmp = document.createElement("style"); // if(opts.title) { tmp = $("style[id='" + opts.title + "-stylesheet']")[0]; }
tmp.setAttribute('type',"text/css"); // if(tmp) { is_new = false; }
if(opts.title) { tmp.setAttribute("id", opts.title + "-stylesheet"); } // else {
} // tmp = document.createElement("style");
if(tmp.styleSheet) { // tmp.setAttribute('type',"text/css");
if(is_new) { // if(opts.title) { tmp.setAttribute("id", opts.title + "-stylesheet"); }
document.getElementsByTagName("head")[0].appendChild(tmp); // }
tmp.styleSheet.cssText = opts.str; // if(tmp.styleSheet) {
} // if(is_new) {
else { // document.getElementsByTagName("head")[0].appendChild(tmp);
tmp.styleSheet.cssText = tmp.styleSheet.cssText + " " + opts.str; // tmp.styleSheet.cssText = opts.str;
} // }
} // else {
else { // tmp.styleSheet.cssText = tmp.styleSheet.cssText + " " + opts.str;
tmp.appendChild(document.createTextNode(opts.str)); // }
document.getElementsByTagName("head")[0].appendChild(tmp); // }
} // else {
return tmp.sheet || tmp.styleSheet; // tmp.appendChild(document.createTextNode(opts.str));
} // document.getElementsByTagName("head")[0].appendChild(tmp);
if(opts.url) { // }
if(document.createStyleSheet) { // return tmp.sheet || tmp.styleSheet;
try { tmp = document.createStyleSheet(opts.url); } catch (e) { } // }
} // if(opts.url) {
else { // if(document.createStyleSheet) {
tmp = document.createElement('link'); // try { tmp = document.createStyleSheet(opts.url); } catch (e) { }
tmp.rel = 'stylesheet'; // }
tmp.type = 'text/css'; // else {
tmp.media = "all"; // tmp = document.createElement('link');
tmp.href = opts.url; // tmp.rel = 'stylesheet';
document.getElementsByTagName("head")[0].appendChild(tmp); // tmp.type = 'text/css';
return tmp.styleSheet; // tmp.media = "all";
} // tmp.href = opts.url;
} // document.getElementsByTagName("head")[0].appendChild(tmp);
// return tmp.styleSheet;
// }
// }
} }
}; };