diff --git a/_config.php b/_config.php index 7a7f03db..a44c8e70 100644 --- a/_config.php +++ b/_config.php @@ -6,8 +6,7 @@ * @package cms */ Director::addRules(50, array( - 'processes//$Action/$ID/$Batch' => 'BatchProcess_Controller', - 'admin/help//$Action/$ID' => 'CMSHelp', + '' => 'RootURLController', 'admin/bulkload//$Action/$ID/$OtherID' => 'BulkLoaderAdmin', 'admin/cms//$Action/$ID/$OtherID' => 'CMSMain', 'dev/buildcache/$Action' => 'RebuildStaticCacheTask', @@ -17,36 +16,6 @@ Director::addRules(1, array( '$URLSegment//$Action/$ID/$OtherID' => 'ModelAsController', )); -CMSMenu::add_director_rules(); - -// Default CMS HTMLEditorConfig -HtmlEditorConfig::get('cms')->setOptions(array( - 'friendly_name' => 'Default CMS', - 'priority' => '50', - 'mode' => 'none', - - 'body_class' => 'typography', - 'document_base_url' => Director::absoluteBaseURL(), - - 'urlconverter_callback' => "nullConverter", - 'setupcontent_callback' => "sapphiremce_setupcontent", - 'cleanup_callback' => "sapphiremce_cleanup", - - 'use_native_selects' => true, // fancy selects are bug as of SS 2.3.0 - 'valid_elements' => "@[id|class|style|title],#a[id|rel|rev|dir|tabindex|accesskey|type|name|href|target|title|class],-strong/-b[class],-em/-i[class],-strike[class],-u[class],#p[id|dir|class|align|style],-ol[class],-ul[class],-li[class],br,img[id|dir|longdesc|usemap|class|src|border|alt=|title|width|height|align],-sub[class],-sup[class],-blockquote[dir|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|dir|id|style],-tr[id|dir|class|rowspan|width|height|align|valign|bgcolor|background|bordercolor|style],tbody[id|class|style],thead[id|class|style],tfoot[id|class|style],#td[id|dir|class|colspan|rowspan|width|height|align|valign|scope|style],-th[id|dir|class|colspan|rowspan|width|height|align|valign|scope|style],caption[id|dir|class],-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align],address[class|align],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|dir|class|align|style],hr[class],dd[id|class|title|dir],dl[id|class|title|dir],dt[id|class|title|dir],@[id,style,class]", - 'extended_valid_elements' => "img[class|src|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name|usemap],iframe[src|name|width|height|align|frameborder|marginwidth|marginheight|scrolling],object[width|height|data|type],param[name|value],map[class|name|id],area[shape|coords|href|target|alt]" -)); - -HtmlEditorConfig::get('cms')->enablePlugins('media', 'fullscreen'); -HtmlEditorConfig::get('cms')->enablePlugins(array('ssbuttons' => '../../../cms/javascript/tinymce_ssbuttons/editor_plugin_src.js')); - -HtmlEditorConfig::get('cms')->insertButtonsBefore('formatselect', 'styleselect'); -HtmlEditorConfig::get('cms')->insertButtonsBefore('advcode', 'ssimage', 'ssflash', 'sslink', 'unlink', 'anchor', 'separator' ); -HtmlEditorConfig::get('cms')->insertButtonsAfter ('advcode', 'fullscreen', 'separator'); - -HtmlEditorConfig::get('cms')->removeButtons('tablecontrols'); -HtmlEditorConfig::get('cms')->addButtonsToLine(3, 'tablecontrols'); - // Register default side reports SS_Report::register("SideReport", "SideReport_EmptyPages"); SS_Report::register("SideReport", "SideReport_RecentlyEdited"); diff --git a/code/CMSMenu.php b/code/CMSMenu.php deleted file mode 100644 index e13e7eff..00000000 --- a/code/CMSMenu.php +++ /dev/null @@ -1,325 +0,0 @@ - 'add', 'item' => new CMSMenuItem(...) ) - * - array('type' => 'remove', 'code' => 'codename' ) - */ - protected static $menu_item_changes = array(); - - /** - * Set to true if clear_menu() is called, to indicate that the default menu shouldn't be - * included - */ - protected static $menu_is_cleared = false; - - /** - * Generate CMS main menu items by collecting valid - * subclasses of {@link LeftAndMain} - */ - public static function populate_menu() { - self::$menu_is_cleared = false; - } - - /** - * Add Director rules for all of the CMS controllers. - */ - public static function add_director_rules() { - array_map(array('self','add_director_rule_for_controller'), self::get_cms_classes()); - } - - /** - * Add a LeftAndMain controller to the CMS menu. - * - * @param string $controllerClass The class name of the controller - * @return The result of the operation - * @todo A director rule is added when a controller link is added, but it won't be removed - * when the item is removed. Functionality needed in {@link Director}. - */ - public static function add_controller($controllerClass) { - if($menuItem = self::menuitem_for_controller($controllerClass)) { - self::add_menu_item_obj($controllerClass, $menuItem); - } - } - - /** - * Return a CMSMenuItem to add the given controller to the CMSMenu - */ - protected static function menuitem_for_controller($controllerClass) { - $urlBase = Object::get_static($controllerClass, 'url_base'); - $urlSegment = Object::get_static($controllerClass, 'url_segment'); - $menuPriority = Object::get_static($controllerClass, 'menu_priority'); - - // Don't add menu items defined the old way - if($urlSegment === null && $controllerClass != "CMSMain") return; - - $link = Controller::join_links($urlBase, $urlSegment) . '/'; - - // doesn't work if called outside of a controller context (e.g. in _config.php) - // as the locale won't be detected properly. Use {@link LeftAndMain->MainMenu()} to update - // titles for existing menu entries - $defaultTitle = LeftAndMain::menu_title_for_class($controllerClass); - $menuTitle = _t("{$controllerClass}.MENUTITLE", $defaultTitle); - - return new CMSMenuItem($menuTitle, $link, $controllerClass, $menuPriority); - } - - /** - * Add the appropriate Director rules for the given controller. - */ - protected static function add_director_rule_for_controller($controllerClass) { - $urlBase = Object::get_static($controllerClass, 'url_base'); - $urlSegment = Object::get_static($controllerClass, 'url_segment'); - $urlRule = Object::get_static($controllerClass, 'url_rule'); - $urlPriority = Object::get_static($controllerClass, 'url_priority'); - - if($urlSegment || $controllerClass == "CMSMain") { - $link = Controller::join_links($urlBase, $urlSegment) . '/'; - - // Make director rule - if($urlRule[0] == '/') $urlRule = substr($urlRule,1); - $rule = $link . '/' . $urlRule; // the / will combine with the / on the end of $link to make a // - Director::addRules($urlPriority, array( - $rule => $controllerClass - )); - } - } - - /** - * Add an arbitrary URL to the CMS menu. - * - * @param string $code A unique identifier (used to create a CSS ID and as it's key in {@link $menu_items} - * @param string $menuTitle The link's title in the CMS menu - * @param string $url The url of the link - * @param integer $priority The menu priority (sorting order) of the menu item. Higher priorities will be further left. - * @return boolean The result of the operation. - */ - public static function add_link($code, $menuTitle, $url, $priority = -1) { - return self::add_menu_item($code, $menuTitle, $url, null, $priority); - } - - /** - * Add a navigation item to the main administration menu showing in the top bar. - * - * uses {@link CMSMenu::$menu_items} - * - * @param string $code Unique identifier for this menu item (e.g. used by {@link replace_menu_item()} and - * {@link remove_menu_item}. Also used as a CSS-class for icon customization. - * @param string $menuTitle Localized title showing in the menu bar - * @param string $url A relative URL that will be linked in the menu bar. - * @param string $controllerClass The controller class for this menu, used to check permisssions. - * If blank, it's assumed that this is public, and always shown to users who - * have the rights to access some other part of the admin area. - * @return boolean Success - */ - public static function add_menu_item($code, $menuTitle, $url, $controllerClass = null, $priority = -1) { - // If a class is defined, then force the use of that as a code. This helps prevent menu item duplication - if($controllerClass) $code = $controllerClass; - - return self::replace_menu_item($code, $menuTitle, $url, $controllerClass, $priority); - } - - /** - * Get a single menu item by its code value. - * - * @param string $code - * @return array - */ - public static function get_menu_item($code) { - $menuItems = self::get_menu_items(); - return (isset($menuItems[$code])) ? $menuItems[$code] : false; - } - - /** - * Get all menu entries. - * - * @return array - */ - public static function get_menu_items() { - $menuItems = array(); - - // Set up default menu items - if(!self::$menu_is_cleared) { - $cmsClasses = self::get_cms_classes(); - foreach($cmsClasses as $cmsClass) { - $menuItem = self::menuitem_for_controller($cmsClass); - if($menuItem) $menuItems[$cmsClass] = $menuItem; - } - } - - // Apply changes - foreach(self::$menu_item_changes as $change) { - switch($change['type']) { - case 'add': - $menuItems[$change['code']] = $change['item']; - break; - - case 'remove': - unset($menuItems[$change['code']]); - break; - - default: - user_error("Bad menu item change type {$change[type]}", E_USER_WARNING); - } - } - - // Sort menu items according to priority - $menuPriority = array(); - $i = 0; - foreach($menuItems as $key => $menuItem) { - $i++; - // This funny litle formula ensures that the first item added with the same priority will be left-most. - $menuPriority[$key] = $menuItem->priority*100 - $i; - } - array_multisort($menuPriority, SORT_DESC, $menuItems); - - return $menuItems; - } - - /** - * Get all menu items that the passed member can view. - * Defaults to {@link Member::currentUser()}. - * - * @param Member $member - * @return array - */ - public static function get_viewable_menu_items($member = null) { - if(!$member && $member !== FALSE) { - $member = Member::currentUser(); - } - - $viewableMenuItems = array(); - $allMenuItems = self::get_menu_items(); - if($allMenuItems) foreach($allMenuItems as $code => $menuItem) { - // exclude all items which have a controller to perform permission - // checks on - if($menuItem->controller) { - $controllerObj = singleton($menuItem->controller); - // Necessary for canView() to have request data available, - // e.g. to check permissions against LeftAndMain->currentPage() - $controllerObj->setRequest(Controller::curr()->getRequest()); - if(!$controllerObj->canView($member)) continue; - } - - $viewableMenuItems[$code] = $menuItem; - } - - return $viewableMenuItems; - } - - /** - * Removes an existing item from the menu. - * - * @param string $code Unique identifier for this menu item - */ - public static function remove_menu_item($code) { - self::$menu_item_changes[] = array('type' => 'remove', 'code' => $code); - } - - /** - * Clears the entire menu - */ - public static function clear_menu() { - self::$menu_item_changes = array(); - self::$menu_is_cleared = true; - } - - /** - * Replace a navigation item to the main administration menu showing in the top bar. - * - * @param string $code Unique identifier for this menu item (e.g. used by {@link replace_menu_item()} and - * {@link remove_menu_item}. Also used as a CSS-class for icon customization. - * @param string $menuTitle Localized title showing in the menu bar - * @param string $url A relative URL that will be linked in the menu bar. - * Make sure to add a matching route via {@link Director::addRules()} to this url. - * @param string $controllerClass The controller class for this menu, used to check permisssions. - * If blank, it's assumed that this is public, and always shown to users who - * have the rights to access some other part of the admin area. - * @return boolean Success - */ - public static function replace_menu_item($code, $menuTitle, $url, $controllerClass = null, $priority = -1) { - self::$menu_item_changes[] = array( - 'type' => 'add', - 'code' => $code, - 'item' => new CMSMenuItem($menuTitle, $url, $controllerClass, $priority), - ); - } - - /** - * Add a previously built menuitem object to the menu - */ - protected static function add_menu_item_obj($code, $cmsMenuItem) { - self::$menu_item_changes[] = array( - 'type' => 'add', - 'code' => $code, - 'item' => $cmsMenuItem, - ); - } - - /** - * A utility funciton to retrieve subclasses of a given class that - * are instantiable (ie, not abstract) and have a valid menu title. - * - * @todo A variation of this function could probably be moved to {@link ClassInfo} - * @param string $root The root class to begin finding subclasses - * @param boolean $recursive Look for subclasses recursively? - * @return array Valid, unique subclasses - */ - public static function get_cms_classes($root = 'LeftAndMain', $recursive = true) { - $subClasses = array_values(ClassInfo::subclassesFor($root)); - foreach($subClasses as $className) { - if($recursive && $className != $root) { - $subClasses = array_merge($subClasses, array_values(ClassInfo::subclassesFor($className))); - } - } - $subClasses = array_unique($subClasses); - foreach($subClasses as $key => $className) { - // Remove abstract classes and LeftAndMain - $classReflection = new ReflectionClass($className); - if( - !$classReflection->isInstantiable() - || 'LeftAndMain' == $className - || ClassInfo::classImplements($className, 'TestOnly') - ) { - unset($subClasses[$key]); - } - } - - return $subClasses; - } - - /** - * IteratorAggregate Interface Method. Iterates over the menu items. - */ - function getIterator() { - return new ArrayIterator(self::get_menu_items()); - } - - /** - * Provide menu titles to the i18n entity provider - */ - function provideI18nEntities() { - $cmsClasses = self::get_cms_classes(); - $entities = array(); - foreach($cmsClasses as $cmsClass) { - $defaultTitle = LeftAndMain::menu_title_for_class($cmsClass); - $ownerModule = i18n::get_owner_module($cmsClass); - $entities["{$cmsClass}.MENUTITLE"] = array($defaultTitle, PR_HIGH, 'Menu title', $ownerModule); - } - return $entities; - } -} -?> \ No newline at end of file diff --git a/code/CMSMenuItem.php b/code/CMSMenuItem.php deleted file mode 100644 index 4a6cd654..00000000 --- a/code/CMSMenuItem.php +++ /dev/null @@ -1,50 +0,0 @@ -title = $title; - $this->url = $url; - $this->controller = $controller; - $this->priority = $priority; - parent::__construct(); - } - -} -?> \ No newline at end of file diff --git a/code/GroupImportForm.php b/code/GroupImportForm.php deleted file mode 100644 index 3cab0c7d..00000000 --- a/code/GroupImportForm.php +++ /dev/null @@ -1,90 +0,0 @@ -Import one or more groups in CSV format (comma-separated values). Show advanced usage

' - ); - $helpHtml .= _t( - 'GroupImportForm.Help2', -'
-

Advanced usage

- -
'); - - $importer = new GroupCsvBulkLoader(); - $importSpec = $importer->getImportSpec(); - $helpHtml = sprintf($helpHtml, implode(', ', array_keys($importSpec['fields']))); - - $fields = new FieldSet( - new LiteralField('Help', $helpHtml), - $fileField = new FileField( - 'CsvFile', - _t( - 'SecurityAdmin_MemberImportForm.FileFieldLabel', - 'CSV File (Allowed extensions: *.csv)' - ) - ) - ); - $fileField->getValidator()->setAllowedExtensions(array('csv')); - } - - if(!$actions) $actions = new FieldSet( - new FormAction('doImport', _t('SecurityAdmin_MemberImportForm.BtnImport', 'Import')) - ); - - if(!$validator) $validator = new RequiredFields('CsvFile'); - - parent::__construct($controller, $name, $fields, $actions, $validator); - - $this->addExtraClass('import-form'); - } - - function doImport($data, $form) { - $loader = new GroupCsvBulkLoader(); - - // load file - $result = $loader->load($data['CsvFile']['tmp_name']); - - // result message - $msgArr = array(); - if($result->CreatedCount()) $msgArr[] = sprintf( - _t('GroupImportForm.ResultCreated', 'Created %d groups'), - $result->CreatedCount() - ); - if($result->UpdatedCount()) $msgArr[] = sprintf( - _t('GroupImportForm.ResultUpdated', 'Updated %d groups'), - $result->UpdatedCount() - ); - if($result->DeletedCount()) $msgArr[] = sprintf( - _t('GroupImportForm.ResultDeleted', 'Deleted %d groups'), - $result->DeletedCount() - ); - $msg = ($msgArr) ? implode(',', $msgArr) : _t('MemberImportForm.ResultNone', 'No changes'); - - $this->sessionMessage($msg, 'good'); - - $this->redirectBack(); - } - -} -?> \ No newline at end of file diff --git a/code/LeftAndMain.php b/code/LeftAndMain.php deleted file mode 100644 index aac218e2..00000000 --- a/code/LeftAndMain.php +++ /dev/null @@ -1,1325 +0,0 @@ - array(), - 'css' => array(), - 'themedcss' => array(), - ); - - /** - * @param Member $member - * @return boolean - */ - function canView($member = null) { - if(!$member && $member !== FALSE) { - $member = Member::currentUser(); - } - - // cms menus only for logged-in members - if(!$member) return false; - - // alternative decorated checks - if($this->hasMethod('alternateAccessCheck')) { - $alternateAllowed = $this->alternateAccessCheck(); - if($alternateAllowed === FALSE) return false; - } - - // Default security check for LeftAndMain sub-class permissions - if(!Permission::checkMember($member, "CMS_ACCESS_$this->class") && - !Permission::checkMember($member, "CMS_ACCESS_LeftAndMain")) { - return false; - } - - return true; - } - - /** - * @uses LeftAndMainDecorator->init() - * @uses LeftAndMainDecorator->accessedCMS() - * @uses CMSMenu - */ - function init() { - parent::init(); - - SSViewer::setOption('rewriteHashlinks', false); - - // set language - $member = Member::currentUser(); - if(!empty($member->Locale)) i18n::set_locale($member->Locale); - if(!empty($member->DateFormat)) i18n::set_date_format($member->DateFormat); - if(!empty($member->TimeFormat)) i18n::set_time_format($member->TimeFormat); - - // can't be done in cms/_config.php as locale is not set yet - CMSMenu::add_link( - 'Help', - _t('LeftAndMain.HELP', 'Help', PR_HIGH, 'Menu title'), - self::$help_link - ); - - // set reading lang - if(class_exists('Translatable') && Object::has_extension('SiteTree', 'Translatable') && !$this->isAjax()) { - Translatable::choose_site_locale(array_keys(Translatable::get_existing_content_languages('SiteTree'))); - } - - // Allow customisation of the access check by a decorator - // Also all the canView() check to execute Director::redirect() - if(!$this->canView() && !$this->response->isFinished()) { - // When access /admin/, we should try a redirect to another part of the admin rather than be locked out - $menu = $this->MainMenu(); - foreach($menu as $candidate) { - if( - $candidate->Link && - $candidate->Link != $this->Link() - && $candidate->MenuItem->controller - && singleton($candidate->MenuItem->controller)->canView() - ) { - return Director::redirect($candidate->Link); - } - } - - if(Member::currentUser()) { - Session::set("BackURL", null); - } - - // if no alternate menu items have matched, return a permission error - $messageSet = array( - 'default' => _t('LeftAndMain.PERMDEFAULT',"Please choose an authentication method and enter your credentials to access the CMS."), - 'alreadyLoggedIn' => _t('LeftAndMain.PERMALREADY',"I'm sorry, but you can't access that part of the CMS. If you want to log in as someone else, do so below"), - 'logInAgain' => _t('LeftAndMain.PERMAGAIN',"You have been logged out of the CMS. If you would like to log in again, enter a username and password below."), - ); - - return Security::permissionFailure($this, $messageSet); - } - - // Don't continue if there's already been a redirection request. - if(Director::redirected_to()) return; - - // Audit logging hook - if(empty($_REQUEST['executeForm']) && !$this->isAjax()) $this->extend('accessedCMS'); - - // Set the members html editor config - HtmlEditorConfig::set_active(Member::currentUser()->getHtmlEditorConfigForCMS()); - - - // Set default values in the config if missing. These things can't be defined in the config - // file because insufficient information exists when that is being processed - $htmlEditorConfig = HtmlEditorConfig::get_active(); - $htmlEditorConfig->setOption('language', i18n::get_tinymce_lang()); - if(!$htmlEditorConfig->getOption('content_css')) { - $cssFiles = 'cms/css/editor.css'; - - // Use theme from the site config - if(($config = SiteConfig::current_site_config()) && $config->Theme) { - $theme = $config->Theme; - } elseif(SSViewer::current_theme()) { - $theme = SSViewer::current_theme(); - } else { - $theme = false; - } - - if($theme) $cssFiles .= ',' . THEMES_DIR . "/{$theme}/css/editor.css"; - else if(project()) $cssFiles .= ',' . project() . '/css/editor.css'; - - $htmlEditorConfig->setOption('content_css', $cssFiles); - } - - - Requirements::css(CMS_DIR . '/css/typography.css'); - Requirements::css(CMS_DIR . '/css/layout.css'); - Requirements::css(CMS_DIR . '/css/cms_left.css'); - Requirements::css(CMS_DIR . '/css/cms_right.css'); - Requirements::css(SAPPHIRE_DIR . '/css/Form.css'); - - if(isset($_REQUEST['debug_firebug'])) { - // Firebug is a useful console for debugging javascript - // Its available as a Firefox extension or a javascript library - // for easy inclusion in other browsers (just append ?debug_firebug=1 to the URL) - Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/firebug-lite/firebug.js'); - } - - Requirements::javascript(SAPPHIRE_DIR . '/javascript/prototypefix/intro.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(SAPPHIRE_DIR . '/javascript/jquery_improvements.js'); - - Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/jquery-ui.js'); //import all of jquery ui - - Requirements::javascript(CMS_DIR . '/thirdparty/jquery-layout/jquery.layout.js'); - Requirements::javascript(CMS_DIR . '/thirdparty/jquery-layout/jquery.layout.state.js'); - Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/json-js/json2.js'); - Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-metadata/jquery.metadata.js'); - Requirements::javascript(CMS_DIR . '/javascript/jquery-fitheighttoparent/jquery.fitheighttoparent.js'); - - Requirements::javascript(CMS_DIR . '/javascript/ssui.core.js'); - // @todo Load separately so the CSS files can be inlined - Requirements::css(SAPPHIRE_DIR . '/thirdparty/jquery-ui-themes/smoothness/jquery.ui.all.css'); - - // entwine - Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-entwine/dist/jquery.entwine-dist.js'); - - // Required for TreeTools panel above tree - Requirements::javascript(SAPPHIRE_DIR . '/javascript/TabSet.js'); - - Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/behaviour/behaviour.js'); - Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-cookie/jquery.cookie.js'); - Requirements::javascript(CMS_DIR . '/thirdparty/jquery-notice/jquery.notice.js'); - Requirements::javascript(SAPPHIRE_DIR . '/javascript/jquery-ondemand/jquery.ondemand.js'); - Requirements::javascript(CMS_DIR . '/javascript/jquery-changetracker/lib/jquery.changetracker.js'); - Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang'); - Requirements::add_i18n_javascript(CMS_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 . '/jstree/jquery.jstree.js'); - Requirements::css(THIRDPARTY_DIR . '/jstree/themes/apple/style.css'); - - Requirements::javascript(CMS_DIR . '/javascript/LeftAndMain.js'); - Requirements::javascript(CMS_DIR . '/javascript/LeftAndMain.Tree.js'); - Requirements::javascript(CMS_DIR . '/javascript/LeftAndMain.EditForm.js'); - Requirements::javascript(CMS_DIR . '/javascript/LeftAndMain.AddForm.js'); - Requirements::javascript(CMS_DIR . '/javascript/LeftAndMain.BatchActions.js'); - - // navigator - Requirements::css(CMS_DIR . '/css/SilverStripeNavigator.css'); - Requirements::javascript(CMS_DIR . '/javascript/SilverStripeNavigator.js'); - - Requirements::themedCSS('typography'); - - foreach (self::$extra_requirements['javascript'] as $file) { - Requirements::javascript($file[0]); - } - - foreach (self::$extra_requirements['css'] as $file) { - Requirements::css($file[0], $file[1]); - } - - foreach (self::$extra_requirements['themedcss'] as $file) { - Requirements::themedCSS($file[0], $file[1]); - } - - Requirements::css(CMS_DIR . '/css/unjquery.css'); - - // Javascript combined files - Requirements::combine_files( - 'base.js', - array( - 'sapphire/thirdparty/prototype/prototype.js', - 'sapphire/thirdparty/behaviour/behaviour.js', - 'sapphire/thirdparty/jquery/jquery.js', - 'sapphire/thirdparty/jquery-livequery/jquery.livequery.js', - 'sapphire/javascript/jquery-ondemand/jquery.ondemand.js', - 'sapphire/thirdparty/jquery-ui/jquery-ui.js', - 'sapphire/javascript/i18n.js', - ) - ); - - Requirements::combine_files( - 'leftandmain.js', - array( - 'sapphire/thirdparty/scriptaculous/effects.js', - 'sapphire/thirdparty/scriptaculous/dragdrop.js', - 'sapphire/thirdparty/scriptaculous/controls.js', - 'cms/javascript/LeftAndMain.js', - 'sapphire/thirdparty/jstree/jquery.jstree.js', - 'sapphire/javascript/TreeDropdownField.js', - 'cms/javascript/ThumbnailStripField.js', - ) - ); - - $dummy = null; - $this->extend('init', $dummy); - - // The user's theme shouldn't affect the CMS, if, for example, they have replaced - // TableListField.ss or Form.ss. - SSViewer::set_theme(null); - } - - - /** - * If this is set to true, the "switchView" context in the - * template is shown, with links to the staging and publish site. - * - * @return boolean - */ - function ShowSwitchView() { - return false; - } - - //------------------------------------------------------------------------------------------// - // Main controllers - - /** - * You should implement a Link() function in your subclass of LeftAndMain, - * to point to the URL of that particular controller. - * - * @return string - */ - public function Link($action = null) { - // Handle missing url_segments - if(!$this->stat('url_segment', true)) - self::$url_segment = $this->class; - return Controller::join_links( - $this->stat('url_base', true), - $this->stat('url_segment', true), - '/', // trailing slash needed if $action is null! - "$action" - ); - } - - /** - * Returns the menu title for the given LeftAndMain subclass. - * Implemented static so that we can get this value without instantiating an object. - * Menu title is *not* internationalised. - */ - static function menu_title_for_class($class) { - $title = eval("return $class::\$menu_title;"); - if(!$title) $title = preg_replace('/Admin$/', '', $class); - return $title; - } - - public function show($request) { - // TODO Necessary for TableListField URLs to work properly - if($request->param('ID')) $this->setCurrentPageID($request->param('ID')); - - if($this->isAjax()) { - SSViewer::setOption('rewriteHashlinks', false); - $form = $this->getEditForm($request->param('ID')); - $content = $form->formHtmlContent(); - } else { - // Rendering is handled by template, which will call EditForm() eventually - $content = $this->renderWith($this->getViewer('show')); - } - - if($this->ShowSwitchView()) { - $content .= '
' . $this->SwitchView() . '
'; - } - - return $content; - } - - /** - * @deprecated 2.4 Please use show() - */ - public function getitem($request) { - $form = $this->getEditForm($request->getVar('ID')); - return $form->formHtmlContent(); - } - - //------------------------------------------------------------------------------------------// - // Main UI components - - /** - * Returns the main menu of the CMS. This is also used by init() - * to work out which sections the user has access to. - * - * @return DataObjectSet - */ - public function MainMenu() { - // Don't accidentally return a menu if you're not logged in - it's used to determine access. - if(!Member::currentUser()) return new DataObjectSet(); - - // Encode into DO set - $menu = new DataObjectSet(); - $menuItems = CMSMenu::get_viewable_menu_items(); - if($menuItems) foreach($menuItems as $code => $menuItem) { - // alternate permission checks (in addition to LeftAndMain->canView()) - if( - isset($menuItem->controller) - && $this->hasMethod('alternateMenuDisplayCheck') - && !$this->alternateMenuDisplayCheck($menuItem->controller) - ) { - continue; - } - - $linkingmode = ""; - - if(strpos($this->Link(), $menuItem->url) !== false) { - if($this->Link() == $menuItem->url) { - $linkingmode = "current"; - - // default menu is the one with a blank {@link url_segment} - } else if(singleton($menuItem->controller)->stat('url_segment') == '') { - if($this->Link() == $this->stat('url_base').'/') $linkingmode = "current"; - - } else { - $linkingmode = "current"; - } - } - - // already set in CMSMenu::populate_menu(), but from a static pre-controller - // context, so doesn't respect the current user locale in _t() calls - as a workaround, - // we simply call LeftAndMain::menu_title_for_class() again - // if we're dealing with a controller - if($menuItem->controller) { - $defaultTitle = LeftAndMain::menu_title_for_class($menuItem->controller); - $title = _t("{$menuItem->controller}.MENUTITLE", $defaultTitle); - } else { - $title = $menuItem->title; - } - - $menu->push(new ArrayData(array( - "MenuItem" => $menuItem, - "Title" => Convert::raw2xml($title), - "Code" => $code, - "Link" => $menuItem->url, - "LinkingMode" => $linkingmode - ))); - } - - // if no current item is found, assume that first item is shown - //if(!isset($foundCurrent)) - return $menu; - } - - public function CMSTopMenu() { - return $this->renderWith(array('CMSTopMenu_alternative','CMSTopMenu')); - } - - /** - * Return a list of appropriate templates for this class, with the given suffix - */ - protected function getTemplatesWithSuffix($suffix) { - $classes = array_reverse(ClassInfo::ancestry($this->class)); - foreach($classes as $class) { - $templates[] = $class . $suffix; - if($class == 'LeftAndMain') break; - } - return $templates; - } - - public function Left() { - return $this->renderWith($this->getTemplatesWithSuffix('_left')); - } - - public function Right() { - return $this->renderWith($this->getTemplatesWithSuffix('_right')); - } - - public function getRecord($id) { - $className = $this->stat('tree_class'); - if($id instanceof $className) { - return $id; - } else if(is_numeric($id)) { - return DataObject::get_by_id($className, $id); - } else { - return false; - } - } - - /** - * @return String HTML - */ - public function SiteTreeAsUL() { - return $this->getSiteTreeFor($this->stat('tree_class')); - } - - /** - * Get a site tree HTML listing which displays the nodes under the given criteria. - * - * @param $className The class of the root object - * @param $rootID The ID of the root object. If this is null then a complete tree will be - * shown - * @param $childrenMethod The method to call to get the children of the tree. For example, - * Children, AllChildrenIncludingDeleted, or AllHistoricalChildren - * @return String Nested unordered list with links to each page - */ - function getSiteTreeFor($className, $rootID = null, $childrenMethod = null, $numChildrenMethod = null, $filterFunction = null, $minNodeCount = 30) { - // Default childrenMethod and numChildrenMethod - if (!$childrenMethod) $childrenMethod = 'AllChildrenIncludingDeleted'; - if (!$numChildrenMethod) $numChildrenMethod = 'numChildren'; - - // Get the tree root - $obj = $rootID ? $this->getRecord($rootID) : singleton($className); - - // Mark the nodes of the tree to return - if ($filterFunction) $obj->setMarkingFilterFunction($filterFunction); - - $obj->markPartialTree($minNodeCount, $this, $childrenMethod, $numChildrenMethod); - - // Ensure current page is exposed - if($p = $this->currentPage()) $obj->markToExpose($p); - - // NOTE: SiteTree/CMSMain coupling :-( - SiteTree::prepopuplate_permission_cache('CanEditType', $obj->markedNodeIDs(), 'SiteTree::can_edit_multiple'); - - // getChildrenAsUL is a flexible and complex way of traversing the tree - $titleEval = ' - "
  • ID\" data-id=\"$child->ID\" class=\"" . $child->CMSTreeClasses($extraArg) . "\">" . - " " . - "Link(),0,-1), "show", $child->ID) . "\" title=\"' - . _t('LeftAndMain.PAGETYPE','Page type: ') - . '".$child->class."\" > " . ($child->TreeTitle) . - "" - '; - - $html = $obj->getChildrenAsUL( - "", - $titleEval, - $this, - true, - $childrenMethod, - $numChildrenMethod, - $minNodeCount - ); - - // Wrap the root if needs be. - if(!$rootID) { - $rootLink = $this->Link('show') . '/root'; - - // This lets us override the tree title with an extension - if($this->hasMethod('getCMSTreeTitle') && $customTreeTitle = $this->getCMSTreeTitle()) { - $treeTitle = $customTreeTitle; - } else { - $siteConfig = SiteConfig::current_site_config(); - $treeTitle = $siteConfig->Title; - } - - $html = ""; - } - - return $html; - } - - /** - * Get a subtree underneath the request param 'ID'. - * If ID = 0, then get the whole tree. - */ - public function getsubtree($request) { - if($filterClass = $request->requestVar('FilterClass')) { - if(!is_subclass_of($filterClass, 'CMSSiteTreeFilter')) { - throw new Exception(sprintf('Invalid filter class passed: %s', $filterClass)); - } - - $filter = new $filterClass($request->requestVars()); - } else { - $filter = null; - } - - $html = $this->getSiteTreeFor( - $this->stat('tree_class'), - $request->getVar('ID'), - ($filter) ? $filter->getChildrenMethod() : null, - null, - ($filter) ? array($filter, 'isPageIncluded') : null, - $request->getVar('minNodeCount') - ); - - // Trim off the outer tag - $html = preg_replace('/^[\s\t\r\n]*]*>/','', $html); - $html = preg_replace('/<\/ul[^>]*>[\s\t\r\n]*$/','', $html); - - return $html; - } - - /** - * Save handler - */ - public function save($data, $form) { - $className = $this->stat('tree_class'); - - // Existing or new record? - $SQL_id = Convert::raw2sql($data['ID']); - if(substr($SQL_id,0,3) != 'new') { - $record = DataObject::get_by_id($className, $SQL_id); - if($record && !$record->canEdit()) return Security::permissionFailure($this); - } else { - if(!singleton($this->stat('tree_class'))->canCreate()) return Security::permissionFailure($this); - $record = $this->getNewItem($SQL_id, false); - } - - // save form data into record - $form->saveInto($record, true); - $record->write(); - $this->extend('onAfterSave', $record); - - $this->response->addHeader('X-Status', _t('LeftAndMain.SAVEDUP')); - - // write process might've changed the record, so we reload before returning - $form = $this->getEditForm($record->ID); - - return $form->formHtmlContent(); - } - - /** - * Update the position and parent of a tree node. - * Only saves the node if changes were made. - * - * Required data: - * - 'ID': The moved node - * - 'ParentID': New parent relation of the moved node (0 for root) - * - 'SiblingIDs': Array of all sibling nodes to the moved node (incl. the node itself). - * In case of a 'ParentID' change, relates to the new siblings under the new parent. - * - * @return SS_HTTPResponse JSON string with a - */ - public function savetreenode($request) { - if (!Permission::check('SITETREE_REORGANISE') && !Permission::check('ADMIN')) { - $this->response->setStatusCode( - 403, - _t('LeftAndMain.CANT_REORGANISE',"You do not have permission to rearange the site tree. Your change was not saved.") - ); - return; - } - - $className = $this->stat('tree_class'); - $statusUpdates = array('modified'=>array()); - $id = $request->requestVar('ID'); - $parentID = $request->requestVar('ParentID'); - $siblingIDs = $request->requestVar('SiblingIDs'); - $statusUpdates = array('modified'=>array()); - if(!is_numeric($id) || !is_numeric($parentID)) throw new InvalidArgumentException(); - - $node = DataObject::get_by_id($className, $id); - if($node && !$node->canEdit()) return Security::permissionFailure($this); - - if(!$node) { - $this->response->setStatusCode( - 500, - _t( - 'LeftAndMain.PLEASESAVE', - "Please Save Page: This page could not be upated because it hasn't been saved yet." - ) - ); - return; - } - - // Update hierarchy (only if ParentID changed) - if($node->ParentID != $parentID) { - $node->ParentID = (int)$parentID; - $node->write(); - - $statusUpdates['modified'][$node->ID] = array( - 'TreeTitle'=>$node->TreeTitle - ); - - // Update all dependent pages - if($virtualPages = DataObject::get("VirtualPage", "\"CopyContentFromID\" = $node->ID")) { - foreach($virtualPages as $virtualPage) { - $statusUpdates['modified'][$virtualPage->ID] = array( - 'TreeTitle' => $virtualPage->TreeTitle() - ); - } - } - - $this->response->addHeader('X-Status', _t('LeftAndMain.SAVED','saved')); - } - - // Update sorting - if(is_array($siblingIDs)) { - $counter = 0; - foreach($siblingIDs as $id) { - if($id == $node->ID) { - $node->Sort = ++$counter; - $node->write(); - $statusUpdates['modified'][$node->ID] = array( - 'TreeTitle' => $node->TreeTitle - ); - } else if(is_numeric($id)) { - // Nodes that weren't "actually moved" shouldn't be registered as - // having been edited; do a direct SQL update instead - ++$counter; - DB::query(sprintf("UPDATE \"%s\" SET \"Sort\" = %d WHERE \"ID\" = '%d'", $className, $counter, $id)); - } - } - - $this->response->addHeader('X-Status', _t('LeftAndMain.SAVED','saved')); - } - - return Convert::raw2json($statusUpdates); - } - - public function CanOrganiseSitetree() { - return !Permission::check('SITETREE_REORGANISE') && !Permission::check('ADMIN') ? false : true; - } - - /** - * Retrieves an edit form, either for display, or to process submitted data. - * Also used in the template rendered through {@link Right()} in the $EditForm placeholder. - * - * This is a "pseudo-abstract" methoed, usually connected to a {@link getEditForm()} - * method in an entwine subclass. This method can accept a record identifier, - * selected either in custom logic, or through {@link currentPageID()}. - * The form usually construct itself from {@link DataObject->getCMSFields()} - * for the specific managed subclass defined in {@link LeftAndMain::$tree_class}. - * - * @param HTTPRequest $request Optionally contains an identifier for the - * record to load into the form. - * @return Form Should return a form regardless wether a record has been found. - * Form might be readonly if the current user doesn't have the permission to edit - * the record. - */ - /** - * @return Form - */ - function EditForm($request = null) { - return $this->getEditForm(); - } - - public function getEditForm($id = null) { - if(!$id) $id = $this->currentPageID(); - - if(is_object($id)) { - $record = $id; - } else { - $record = ($id && $id != "root") ? $this->getRecord($id) : null; - if($record && !$record->canView()) return Security::permissionFailure($this); - } - - if($record) { - $fields = $record->getCMSFields(); - if ($fields == null) { - user_error( - "getCMSFields() returned null - it should return a FieldSet object. - Perhaps you forgot to put a return statement at the end of your method?", - E_USER_ERROR - ); - } - - // Add hidden fields which are required for saving the record - // and loading the UI state - if(!$fields->dataFieldByName('ClassName')) { - $fields->push(new HiddenField('ClassName')); - } - if( - Object::has_extension($this->stat('tree_class'), 'Hierarchy') - && !$fields->dataFieldByName('ParentID') - ) { - $fields->push(new HiddenField('ParentID')); - } - - if($record->hasMethod('getAllCMSActions')) { - $actions = $record->getAllCMSActions(); - } else { - $actions = $record->getCMSActions(); - // add default actions if none are defined - if(!$actions || !$actions->Count()) { - if($record->canEdit()) { - $actions->push(new FormAction('save',_t('CMSMain.SAVE','Save'))); - } - } - } - - $form = new Form($this, "EditForm", $fields, $actions); - $form->loadDataFrom($record); - - // Add a default or custom validator. - // @todo Currently the default Validator.js implementation - // adds javascript to the document body, meaning it won't - // be included properly if the associated fields are loaded - // through ajax. This means only serverside validation - // will kick in for pages+validation loaded through ajax. - // This will be solved by using less obtrusive javascript validation - // in the future, see http://open.silverstripe.com/ticket/2915 and - // http://open.silverstripe.com/ticket/3386 - if($record->hasMethod('getCMSValidator')) { - $validator = $record->getCMSValidator(); - // The clientside (mainly LeftAndMain*.js) rely on ajax responses - // which can be evaluated as javascript, hence we need - // to override any global changes to the validation handler. - $validator->setJavascriptValidationHandler('prototype'); - $form->setValidator($validator); - } else { - $form->unsetValidator(); - } - - if(!$record->canEdit()) { - $readonlyFields = $form->Fields()->makeReadonly(); - $form->setFields($readonlyFields); - } - } else { - $form = $this->RootForm(); - } - - return $form; - } - - function RootForm() { - return $this->EmptyForm(); - } - - /** - * Returns a placeholder form, used by {@link getEditForm()} if no record is selected. - * Our javascript logic always requires a form to be present in the CMS interface. - * - * @return Form - */ - function EmptyForm() { - $form = new Form( - $this, - "EditForm", - new FieldSet( - new HeaderField( - 'WelcomeHeader', - $this->getApplicationName() - ), - new LiteralField( - 'WelcomeText', - sprintf('

    %s %s. %s

    ', - _t('LeftAndMain_right.ss.WELCOMETO','Welcome to'), - $this->getApplicationName(), - _t('CHOOSEPAGE','Please choose an item from the left.') - ) - ) - ), - new FieldSet() - ); - $form->unsetValidator(); - - return $form; - } - - /** - * @return Form - */ - function AddForm() { - $class = $this->stat('tree_class'); - - $typeMap = array($class => singleton($class)->i18n_singular_name()); - $typeField = new DropdownField('Type', false, $typeMap, $class); - $form = new Form( - $this, - 'AddForm', - new FieldSet( - new HiddenField('ParentID'), - $typeField->performReadonlyTransformation() - ), - new FieldSet( - new FormAction('doAdd', _t('AssetAdmin_left.ss.GO','Go')) - ) - ); - $form->addExtraClass('actionparams'); - - return $form; - } - - /** - * Add a new group and return its details suitable for ajax. - */ - public function doAdd($data, $form) { - $class = $this->stat('tree_class'); - - // check create permissions - if(!singleton($class)->canCreate()) return Security::permissionFailure($this); - - // check addchildren permissions - if( - singleton($class)->hasDatabaseField('Hierarchy') - && isset($data['ParentID']) - && is_numeric($data['ParentID']) - ) { - $parentRecord = DataObject::get_by_id($class, $data['ParentID']); - if( - $parentRecord->hasMethod('canAddChildren') - && !$parentRecord->canAddChildren() - ) return Security::permissionFailure($this); - } - - $record = Object::create($class); - $form->saveInto($record); - $record->write(); - - // Used in TinyMCE inline folder creation - if(isset($data['returnID'])) { - return $record->ID; - } else if($this->isAjax()) { - $form = $this->getEditForm($record->ID); - return $form->formHtmlContent(); - } else { - return $this->redirect(Controller::join_links($this->Link('show'), $record->ID)); - } - } - - /** - * Batch Actions Handler - */ - function batchactions() { - return new CMSBatchActionHandler($this, 'batchactions', $this->stat('tree_class')); - } - - /** - * @return Form - */ - function BatchActionsForm() { - $actions = $this->batchactions()->batchActionList(); - $actionsMap = array(); - foreach($actions as $action) $actionsMap[$action->Link] = $action->Title; - - $form = new Form( - $this, - 'BatchActionsForm', - new FieldSet( - new LiteralField( - 'Intro', - sprintf('

    %s

    ', - _t( - 'CMSMain_left.ss.SELECTPAGESACTIONS', - 'Select the pages that you want to change & then click an action:' - ) - ) - ), - new HiddenField('csvIDs'), - new DropdownField( - 'Action', - false, - $actionsMap - ) - ), - new FieldSet( - // TODO i18n - new FormAction('submit', "Go") - ) - ); - $form->addExtraClass('actionparams'); - $form->unsetValidator(); - - return $form; - } - - public function myprofile() { - $form = $this->Member_ProfileForm(); - return $this->customise(array( - 'Form' => $form - ))->renderWith('BlankPage'); - } - - public function Member_ProfileForm() { - return new Member_ProfileForm($this, 'Member_ProfileForm', Member::currentUser()); - } - - public function printable() { - $form = $this->getEditForm($this->currentPageID()); - if(!$form) return false; - - $form->transform(new PrintableTransformation()); - $form->setActions(null); - - Requirements::clear(); - Requirements::css(CMS_DIR . '/css/LeftAndMain_printable.css'); - return array( - "PrintForm" => $form - ); - } - - /** - * Identifier for the currently shown record, - * in most cases a database ID. Inspects the following - * sources (in this order): - * - GET/POST parameter named 'ID' - * - URL parameter named 'ID' - * - Session value namespaced by classname, e.g. "CMSMain.currentPage" - * - * @return int - */ - public function currentPageID() { - if($this->request->requestVar('ID')) { - return $this->request->requestVar('ID'); - } elseif ($this->request->param('ID') && is_numeric($this->request->param('ID'))) { - return $this->request->param('ID'); - } elseif(Session::get("{$this->class}.currentPage")) { - return Session::get("{$this->class}.currentPage"); - } else { - return null; - } - } - - /** - * Forces the current page to be set in session, - * which can be retrieved later through {@link currentPageID()}. - * Keep in mind that setting an ID through GET/POST or - * as a URL parameter will overrule this value. - * - * @param int $id - */ - public function setCurrentPageID($id) { - Session::set("{$this->class}.currentPage", $id); - } - - /** - * Uses {@link getRecord()} and {@link currentPageID()} - * to get the currently selected record. - * - * @return DataObject - */ - public function currentPage() { - return $this->getRecord($this->currentPageID()); - } - - /** - * Compares a given record to the currently selected one (if any). - * Used for marking the current tree node. - * - * @return boolean - */ - public function isCurrentPage(DataObject $record) { - return ($record->ID == $this->currentPageID()); - } - - /** - * Get the staus of a certain page and version. - * - * This function is used for concurrent editing, and providing alerts - * when multiple users are editing a single page. It echoes a json - * encoded string to the UA. - */ - - /** - * Return the CMS's HTML-editor toolbar - */ - public function EditorToolbar() { - return Object::create('HtmlEditorField_Toolbar', $this, "EditorToolbar"); - } - - /** - * Return the version number of this application. - * Uses the subversion path information in /silverstripe_version - * (automacially replaced $URL$ placeholder). - * - * @return string - */ - public function CMSVersion() { - $sapphireVersionFile = file_get_contents(BASE_PATH . '/sapphire/silverstripe_version'); - $cmsVersionFile = file_get_contents(BASE_PATH . '/cms/silverstripe_version'); - - $sapphireVersion = $this->versionFromVersionFile($sapphireVersionFile); - $cmsVersion = $this->versionFromVersionFile($cmsVersionFile); - - return "cms: $cmsVersion, sapphire: $sapphireVersion"; - } - - /** - * Return the version from the content of a silverstripe_version file - */ - public function versionFromVersionFile($fileContent) { - if(preg_match('/\/trunk\/silverstripe_version/', $fileContent)) { - return "trunk"; - } else { - preg_match("/\/(?:branches|tags\/rc|tags\/beta|tags\/alpha|tags)\/([A-Za-z0-9._-]+)\/silverstripe_version/", $fileContent, $matches); - return ($matches) ? $matches[1] : null; - } - } - - /** - * @return array - */ - function SwitchView() { - if($page = $this->currentPage()) { - $nav = SilverStripeNavigator::get_for_record($page); - return $nav['items']; - } - } - - /** - * The application name. Customisable by calling - * LeftAndMain::setApplicationName() - the first parameter. - * - * @var String - */ - 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 $logoText - * @param String $link (Optional) - */ - static function setApplicationName($name, $logoText = null, $link = null) { - self::$application_name = $name; - self::$application_logo_text = $logoText ? $logoText : $name; - if($link) self::$application_link = $link; - } - - /** - * Get the application name. - * @return String - */ - function getApplicationName() { - 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 - */ - function SectionTitle() { - // Get menu - use obj() to cache it in the same place as the template engine - $menu = $this->obj('MainMenu'); - - foreach($menu as $menuItem) { - if($menuItem->LinkingMode == 'current') return $menuItem->Title; - } - } - - /** - * The application logo path. Customisable by calling - * LeftAndMain::setLogo() - the first parameter. - * - * @var unknown_type - */ - static $application_logo = 'cms/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 = 'cms/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 - */ - function MceRoot() { - return MCE_ROOT; - } - - /** - * Register the given javascript file as required in the CMS. - * Filenames should be relative to the base, eg, SAPPHIRE_DIR . '/javascript/loader.js' - */ - public static function require_javascript($file) { - self::$extra_requirements['javascript'][] = array($file); - } - - /** - * Register the given stylesheet file as required. - * - * @param $file String Filenames should be relative to the base, eg, THIRDPARTY_DIR . '/tree/tree.css' - * @param $media String Comma-separated list of media-types (e.g. "screen,projector") - * @see http://www.w3.org/TR/REC-CSS2/media.html - */ - public static function require_css($file, $media = null) { - self::$extra_requirements['css'][] = array($file, $media); - } - - /** - * Register the given "themeable stylesheet" as required. - * Themeable stylesheets have globally unique names, just like templates and PHP files. - * Because of this, they can be replaced by similarly named CSS files in the theme directory. - * - * @param $name String The identifier of the file. For example, css/MyFile.css would have the identifier "MyFile" - * @param $media String Comma-separated list of media-types (e.g. "screen,projector") - */ - static function require_themed_css($name, $media = null) { - self::$extra_requirements['themedcss'][] = array($name, $media); - } - -} - -/** - * @package cms - * @subpackage core - */ -class LeftAndMainMarkingFilter { - - /** - * @var array Request params (unsanitized) - */ - protected $params = array(); - - /** - * @param array $params Request params (unsanitized) - */ - function __construct($params = null) { - $this->ids = array(); - $this->expanded = array(); - $parents = array(); - - $q = $this->getQuery($params); - $res = $q->execute(); - if (!$res) return; - - // And keep a record of parents we don't need to get parents - // of themselves, as well as IDs to mark - foreach($res as $row) { - if ($row['ParentID']) $parents[$row['ParentID']] = true; - $this->ids[$row['ID']] = true; - } - - // We need to recurse up the tree, - // finding ParentIDs for each ID until we run out of parents - while (!empty($parents)) { - $res = DB::query('SELECT "ParentID", "ID" FROM "SiteTree" WHERE "ID" in ('.implode(',',array_keys($parents)).')'); - $parents = array(); - - foreach($res as $row) { - if ($row['ParentID']) $parents[$row['ParentID']] = true; - $this->ids[$row['ID']] = true; - $this->expanded[$row['ID']] = true; - } - } - } - - protected function getQuery($params) { - $where = array(); - - $SQL_params = Convert::raw2sql($params); - if(isset($SQL_params['ID'])) unset($SQL_params['ID']); - foreach($SQL_params as $name => $val) { - switch($name) { - default: - // Partial string match against a variety of fields - if(!empty($val) && singleton("SiteTree")->hasDatabaseField($name)) { - $where[] = "\"$name\" LIKE '%$val%'"; - } - } - } - - return new SQLQuery( - array("ParentID", "ID"), - 'SiteTree', - $where - ); - } - - function mark($node) { - $id = $node->ID; - if(array_key_exists((int) $id, $this->expanded)) $node->markOpened(); - return array_key_exists((int) $id, $this->ids) ? $this->ids[$id] : false; - } -} -?> \ No newline at end of file diff --git a/code/LeftAndMainDecorator.php b/code/LeftAndMainDecorator.php deleted file mode 100644 index 7e6970e0..00000000 --- a/code/LeftAndMainDecorator.php +++ /dev/null @@ -1,21 +0,0 @@ - \ No newline at end of file diff --git a/code/MemberImportForm.php b/code/MemberImportForm.php deleted file mode 100644 index a2423ef6..00000000 --- a/code/MemberImportForm.php +++ /dev/null @@ -1,108 +0,0 @@ -Import members in CSV format (comma-separated values). Show advanced usage

    ' - ); - $helpHtml .= _t( - 'MemberImportForm.Help2', -'
    -

    Advanced usage

    -
      -
    • Allowed columns: %s
    • -
    • Existing members are matched by their unique Code property, and updated with any new values from the imported file.
    • -
    • Groups can be assigned by the Groups column. Groups are identified by their Code property, multiple groups can be separated by comma. Existing group memberships are not cleared.
    • -
    -
    '); - - $importer = new MemberCsvBulkLoader(); - $importSpec = $importer->getImportSpec(); - $helpHtml = sprintf($helpHtml, implode(', ', array_keys($importSpec['fields']))); - - $fields = new FieldSet( - new LiteralField('Help', $helpHtml), - $fileField = new FileField( - 'CsvFile', - _t( - 'SecurityAdmin_MemberImportForm.FileFieldLabel', - 'CSV File (Allowed extensions: *.csv)' - ) - ) - ); - $fileField->getValidator()->setAllowedExtensions(array('csv')); - } - - if(!$actions) $actions = new FieldSet( - new FormAction('doImport', _t('SecurityAdmin_MemberImportForm.BtnImport', 'Import')) - ); - - if(!$validator) $validator = new RequiredFields('CsvFile'); - - - parent::__construct($controller, $name, $fields, $actions, $validator); - - Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-entwine/dist/jquery.entwine-dist.js'); - Requirements::javascript(CMS_DIR . '/javascript/MemberImportForm.js'); - $this->addExtraClass('import-form'); - } - - function doImport($data, $form) { - $loader = new MemberCsvBulkLoader(); - - // optionally set group relation - if($this->group) $loader->setGroups(array($this->group)); - - // load file - $result = $loader->load($data['CsvFile']['tmp_name']); - - // result message - $msgArr = array(); - if($result->CreatedCount()) $msgArr[] = sprintf( - _t('MemberImportForm.ResultCreated', 'Created %d members'), - $result->CreatedCount() - ); - if($result->UpdatedCount()) $msgArr[] = sprintf( - _t('MemberImportForm.ResultUpdated', 'Updated %d members'), - $result->UpdatedCount() - ); - if($result->DeletedCount()) $msgArr[] = sprintf( - _t('MemberImportForm.ResultDeleted', 'Deleted %d members'), - $result->DeletedCount() - ); - $msg = ($msgArr) ? implode(',', $msgArr) : _t('MemberImportForm.ResultNone', 'No changes'); - - $this->sessionMessage($msg, 'good'); - - $this->controller->redirectBack(); - } - - /** - * @param $group Group - */ - function setGroup($group) { - $this->group = $group; - } - - /** - * @return Group - */ - function getGroup($group) { - return $this->group; - } -} -?> \ No newline at end of file diff --git a/code/MemberTableField.php b/code/MemberTableField.php deleted file mode 100755 index 806d18da..00000000 --- a/code/MemberTableField.php +++ /dev/null @@ -1,553 +0,0 @@ -summaryFields(); - $memberDbFields = $SNG_member->db(); - $csvFieldList = array(); - - foreach($memberDbFields as $field => $dbFieldType) { - $csvFieldList[$field] = $field; - } - - if($group) { - if(is_object($group)) { - $this->group = $group; - } elseif(is_numeric($group)) { - $this->group = DataObject::get_by_id('Group', $group); - } - } else if(isset($_REQUEST['ctf'][$this->Name()]["ID"]) && is_numeric($_REQUEST['ctf'][$this->Name()]["ID"])) { - $this->group = DataObject::get_by_id('Group', $_REQUEST['ctf'][$this->Name()]["ID"]); - } - - if(!$hidePassword) { - $fieldList["SetPassword"] = "Password"; - } - - $this->hidePassword = $hidePassword; - - // @todo shouldn't this use $this->group? It's unclear exactly - // what group it should be customising the custom Member set with. - if($members && $group) { - $this->setCustomSourceItems($this->memberListWithGroupID($members, $group)); - } - - parent::__construct($controller, $name, $sourceClass, $fieldList); - - $SQL_search = isset($_REQUEST['MemberSearch']) ? Convert::raw2sql($_REQUEST['MemberSearch']) : null; - if(!empty($_REQUEST['MemberSearch'])) { - $searchFilters = array(); - foreach($SNG_member->searchableFields() as $fieldName => $fieldSpec) { - if(strpos($fieldName, '.') === false) $searchFilters[] = "\"$fieldName\" LIKE '%{$SQL_search}%'"; - } - $this->sourceFilter[] = '(' . implode(' OR ', $searchFilters) . ')'; - } - - if($this->group) { - $groupIDs = array($this->group->ID); - if($this->group->AllChildren()) $groupIDs = array_merge($groupIDs, $this->group->AllChildren()->column('ID')); - $this->sourceFilter[] = sprintf( - '"Group_Members"."GroupID" IN (%s)', - implode(',', $groupIDs) - ); - } - - $this->sourceJoin = " INNER JOIN \"Group_Members\" ON \"MemberID\"=\"Member\".\"ID\""; - $this->setFieldListCsv($csvFieldList); - $this->setPageSize($this->stat('page_size')); - } - - function FieldHolder() { - $ret = parent::FieldHolder(); - - Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/scriptaculous/controls.js"); - Requirements::javascript(CMS_DIR . '/javascript/MemberTableField.js'); - Requirements::javascript(CMS_DIR . "/javascript/MemberTableField_popup.js"); - - return $ret; - } - - function sourceID() { - return ($this->group) ? $this->group->ID : 0; - } - - function AddLink() { - return Controller::join_links($this->Link(), 'add'); - } - - function SearchForm() { - $groupID = (isset($this->group)) ? $this->group->ID : 0; - $query = isset($_GET['MemberSearch']) ? $_GET['MemberSearch'] : null; - - $searchFields = new FieldGroup( - new TextField('MemberSearch', _t('MemberTableField.SEARCH', 'Search'), $query), - new HiddenField("ctf[ID]", '', $groupID), - new HiddenField('MemberFieldName', '', $this->name), - new HiddenField('MemberDontShowPassword', '', $this->hidePassword) - ); - - $actionFields = new LiteralField('MemberFilterButton',''); - - $fieldContainer = new FieldGroup( - $searchFields, - $actionFields - ); - - return $fieldContainer->FieldHolder(); - } - - /** - * Add existing member to group rather than creating a new member - */ - function addtogroup() { - // Protect against CSRF on destructive action - $token = $this->getForm()->getSecurityToken(); - if(!$token->checkRequest($this->controller->getRequest())) return $this->httpError(400); - - $data = $_REQUEST; - $groupID = (isset($data['ctf']['ID'])) ? $data['ctf']['ID'] : null; - - if(!is_numeric($groupID)) { - FormResponse::status_messsage(_t('MemberTableField.ADDINGFIELD', 'Adding failed'), 'bad'); - return; - } - - // Get existing record either by ID or unique identifier. - $identifierField = Member::get_unique_identifier_field(); - $className = self::$data_class; - $record = null; - if(isset($data[$identifierField])) { - $record = DataObject::get_one( - $className, - sprintf('"%s" = \'%s\'', $identifierField, $data[$identifierField]) - ); - - if($record && !$record->canEdit()) return $this->httpError('401'); - } - - // Fall back to creating a new record - if(!$record) $record = new $className(); - - // Update an existing record, or populate a new one. - // If values on an existing (autocompleted) record have been changed, - // they will overwrite current data. We need to unset 'ID' - // record as it points to the group rather than the member record, and would - // cause the member to be written to a potentially existing record. - unset($data['ID']); - $record->update($data); - - // Validate record, mainly password restrictions. - // Note: Doesn't use Member_Validator - $valid = $record->validate(); - if($valid->valid()) { - $record->write(); - $record->Groups()->add($groupID); - - $this->sourceItems(); - - // TODO add javascript to highlight added row (problem: might not show up due to sorting/filtering) - FormResponse::update_dom_id($this->id(), $this->renderWith($this->template), true); - FormResponse::status_message( - _t( - 'MemberTableField.ADDEDTOGROUP','Added member to group' - ), - 'good' - ); - - } else { - $message = sprintf( - _t( - 'MemberTableField.ERRORADDINGUSER', - 'There was an error adding the user to the group: %s' - ), - Convert::raw2xml($valid->starredList()) - ); - - FormResponse::status_message($message, 'bad'); - } - - return FormResponse::respond(); - } - - /** - * Custom delete implementation: - * Remove member from group rather than from the database - */ - function delete() { - // Protect against CSRF on destructive action - $token = $this->getForm()->getSecurityToken(); - // TODO Not sure how this is called, using $_REQUEST to be on the safe side - if(!$token->check($_REQUEST['SecurityID'])) return $this->httpError(400); - - $groupID = Convert::raw2sql($_REQUEST['ctf']['ID']); - $memberID = Convert::raw2sql($_REQUEST['ctf']['childID']); - if(is_numeric($groupID) && is_numeric($memberID)) { - $member = DataObject::get_by_id('Member', $memberID); - $member->Groups()->remove($groupID); - } else { - user_error("MemberTableField::delete: Bad parameters: Group=$groupID, Member=$memberID", E_USER_ERROR); - } - - return FormResponse::respond(); - - } - - /** - * ################################# - * Utility Functions - * ################################# - */ - function getParentClass() { - return 'Group'; - } - - function getParentIdName($childClass, $parentClass) { - return 'GroupID'; - } - - /** - * ################################# - * Custom Functions - * ################################# - */ - - /** - * Customise an existing DataObjectSet of Member - * objects with a GroupID. - * - * @param DataObjectSet $members Set of Member objects to customise - * @param Group $group Group object to customise with - * @return DataObjectSet Customised set of Member objects - */ - function memberListWithGroupID($members, $group) { - $newMembers = new DataObjectSet(); - foreach($members as $member) { - $newMembers->push($member->customise(array('GroupID' => $group->ID))); - } - return $newMembers; - } - - function setGroup($group) { - $this->group = $group; - } - - /** - * @return Group - */ - function getGroup() { - return $this->group; - } - - function setController($controller) { - $this->controller = $controller; - } - - function GetControllerName() { - return $this->controller->class; - } - - /** - * Add existing member to group by name (with JS-autocompletion) - */ - function AddRecordForm() { - $fields = new FieldSet(); - foreach($this->FieldList() as $fieldName => $fieldTitle) { - // If we're adding the set password field, we want to hide the text from any peeping eyes - if($fieldName == 'SetPassword') { - $fields->push(new PasswordField($fieldName)); - } else { - $fields->push(new TextField($fieldName)); - } - } - if($this->group) { - $fields->push(new HiddenField('ctf[ID]', null, $this->group->ID)); - } - $actions = new FieldSet( - new FormAction('addtogroup', _t('MemberTableField.ADD','Add')) - ); - - return new TabularStyle( - new NestedForm( - new Form( - $this, - 'AddRecordForm', - $fields, - $actions - ) - ) - ); - } - - function AddForm() { - $form = parent::AddForm(); - - // Set default groups - also implemented in MemberTableField_Popup::__construct() - if($this->group) { - $groupsField = $form->Fields()->dataFieldByName('Groups'); - // TODO Needs to be a string value (not int) because of TreeMultiselectField->getItems(), - // see http://open.silverstripe.org/ticket/5836 - if($groupsField) $groupsField->setValue((string)$this->group->ID); - } - - return $form; - } - - /** - * Same behaviour as parent class, but adds the - * member to the passed GroupID. - * - * @return string - */ - function saveComplexTableField($data, $form, $params) { - $className = $this->sourceClass(); - $childData = new $className(); - - // Needs to write before saveInto() to ensure the 'Groups' TreeMultiselectField saves - $childData->write(); - - try { - $form->saveInto($childData); - $childData->write(); - } catch(ValidationException $e) { - $form->sessionMessage($e->getResult()->message(), 'bad'); - return Director::redirectBack(); - } - - $closeLink = sprintf( - '(%s)', - _t('ComplexTableField.CLOSEPOPUP', 'Close Popup') - ); - $message = sprintf( - _t('ComplexTableField.SUCCESSADD', 'Added %s %s %s'), - $childData->singular_name(), - '' . htmlspecialchars($childData->Title, ENT_QUOTES, 'UTF-8') . '', - $closeLink - ); - $form->sessionMessage($message, 'good'); - - $this->controller->redirectBack(); - } - - /** - * Cached version for getting the appropraite members for this particular group. - * - * This includes getting inherited groups, such as groups under groups. - */ - function sourceItems() { - // Caching. - if($this->sourceItems) { - return $this->sourceItems; - } - - // Setup limits - $limitClause = ''; - if(isset($_REQUEST['ctf'][$this->Name()]['start']) && is_numeric($_REQUEST['ctf'][$this->Name()]['start'])) { - $limitClause = ($_REQUEST['ctf'][$this->Name()]['start']) . ", {$this->pageSize}"; - } else { - $limitClause = "0, {$this->pageSize}"; - } - - // We use the group to get the members, as they already have the bulk of the look up functions - $start = isset($_REQUEST['ctf'][$this->Name()]['start']) ? $_REQUEST['ctf'][$this->Name()]['start'] : 0; - - $this->sourceItems = false; - - if($this->group) { - $this->sourceItems = $this->group->Members( - $this->pageSize, // limit - $start, // offset - $this->sourceFilter, - $this->sourceSort - ); - } else { - $this->sourceItems = DataObject::get(self::$data_class, - $this->sourceFilter, - $this->sourceSort, - null, - array('limit' => $this->pageSize, 'start' => $start) - ); - } - // Because we are not used $this->upagedSourceItems any more, and the DataObjectSet is usually the source - // that a large member set runs out of memory. we disable it here. - //$this->unpagedSourceItems = $this->group->Members('', '', $this->sourceFilter, $this->sourceSort); - $this->totalCount = ($this->sourceItems) ? $this->sourceItems->TotalItems() : 0; - - return $this->sourceItems; - } - - function TotalCount() { - $this->sourceItems(); // Called for its side-effect of setting total count - return $this->totalCount; - } - - /** - * Handles item requests - * MemberTableField needs its own item request class so that it can overload the delete method - */ - function handleItem($request) { - return new MemberTableField_ItemRequest($this, $request->param('ID')); - } -} - -/** - * Popup window for {@link MemberTableField}. - * @package cms - * @subpackage security - */ -class MemberTableField_Popup extends ComplexTableField_Popup { - - function __construct($controller, $name, $fields, $validator, $readonly, $dataObject) { - $group = ($controller instanceof MemberTableField) ? $controller->getGroup() : $controller->getParent()->getGroup(); - // Set default groups - also implemented in AddForm() - if($group) { - $groupsField = $fields->dataFieldByName('Groups'); - if($groupsField) $groupsField->setValue($group->ID); - } - - parent::__construct($controller, $name, $fields, $validator, $readonly, $dataObject); - } - - function forTemplate() { - $ret = parent::forTemplate(); - - Requirements::css(CMS_DIR . '/css/SecurityAdmin.css'); - Requirements::javascript(CMS_DIR . '/javascript/MemberTableField.js'); - Requirements::javascript(CMS_DIR . '/javascript/MemberTableField_popup.js'); - - return $ret; - } - -} - -/** -* @package cms -* @subpackage security -*/ -class MemberTableField_Item extends ComplexTableField_Item { - - function Actions() { - $actions = parent::Actions(); - - foreach($actions as $action) { - if($action->Name == 'delete') { - if($this->parent->getGroup()) { - $action->TitleText = _t('MemberTableField.DeleteTitleText', - 'Delete from this group', - PR_MEDIUM, - 'Delete button hover text' - ); - } else { - $action->TitleText = _t('MemberTableField.DeleteTitleTextDatabase', - 'Delete from database and all groups', - PR_MEDIUM, - 'Delete button hover text' - ); - } - } - } - - return $actions; - } -} - -/** -* @package cms -* @subpackage security -*/ - -class MemberTableField_ItemRequest extends ComplexTableField_ItemRequest { - - /** - * Deleting an item from a member table field should just remove that member from the group - */ - function delete($request) { - // Protect against CSRF on destructive action - $token = $this->ctf->getForm()->getSecurityToken(); - if(!$token->checkRequest($request)) return $this->httpError('400'); - - if($this->ctf->Can('delete') !== true) { - return false; - } - - // if a group limitation is set on the table, remove relation. - // otherwise remove the record from the database - if($this->ctf->getGroup()) { - $groupID = $this->ctf->sourceID(); - $group = DataObject::get_by_id('Group', $groupID); - - // Remove from group and all child groups - foreach($group->getAllChildren() as $subGroup) { - $this->dataObj()->Groups()->remove($subGroup); - } - $this->dataObj()->Groups()->remove($groupID); - } else { - $this->dataObj()->delete(); - } - } - - function getParent() { - return $this->ctf; - } -} - -?> \ No newline at end of file diff --git a/code/ModelAdmin.php b/code/ModelAdmin.php deleted file mode 100755 index 5127242f..00000000 --- a/code/ModelAdmin.php +++ /dev/null @@ -1,1055 +0,0 @@ - - * Director::addRules(50, array('admin/mymodel/$Class/$Action/$ID' => 'MyModelAdmin')); - * - * - * @todo saving logic (should mostly use Form->saveInto() and iterate over relations) - * @todo ajax form loading and saving - * @todo ajax result display - * @todo relation formfield scaffolding (one tab per relation) - relations don't have DBField sublclasses, we do - * we define the scaffold defaults. can be ComplexTableField instances for a start. - * @todo has_many/many_many relation autocomplete field (HasManyComplexTableField doesn't work well with larger datasets) - * - * Long term TODOs: - * @todo Hook into RESTful interface on DataObjects (yet to be developed) - * @todo Permission control via datamodel and Form class - * - * @uses SearchContext - * - * @package cms - * @subpackage core - */ -abstract class ModelAdmin extends LeftAndMain { - - static $url_rule = '/$Action'; - - /** - * List of all managed {@link DataObject}s in this interface. - * - * Simple notation with class names only: - * - * array('MyObjectClass','MyOtherObjectClass') - * - * - * Extended notation with options (e.g. custom titles): - * - * array( - * 'MyObjectClass' => array('title' => "Custom title") - * ) - * - * - * Available options: - * - 'title': Set custom titles for the tabs or dropdown names - * - 'collection_controller': Set a custom class to use as a collection controller for this model - * - 'record_controller': Set a custom class to use as a record controller for this model - * - * @var array|string - */ - public static $managed_models = null; - - /** - * More actions are dynamically added in {@link defineMethods()} below. - */ - public static $allowed_actions = array( - 'add', - 'edit', - 'delete', - 'import', - 'renderimportform', - 'handleList', - 'handleItem', - 'ImportForm' - ); - - /** - * @param string $collection_controller_class Override for controller class - */ - public static $collection_controller_class = "ModelAdmin_CollectionController"; - - /** - * @param string $collection_controller_class Override for controller class - */ - public static $record_controller_class = "ModelAdmin_RecordController"; - - /** - * Forward control to the default action handler - */ - public static $url_handlers = array( - '$Action' => 'handleAction' - ); - - /** - * Model object currently in manipulation queue. Used for updating Link to point - * to the correct generic data object in generated URLs. - * - * @var string - */ - private $currentModel = false; - - /** - * Change this variable if you don't want the Import from CSV form to appear. - * This variable can be a boolean or an array. - * If array, you can list className you want the form to appear on. i.e. array('myClassOne','myClasstwo') - */ - public $showImportForm = true; - - /** - * List of all {@link DataObject}s which can be imported through - * a subclass of {@link BulkLoader} (mostly CSV data). - * By default {@link CsvBulkLoader} is used, assuming a standard mapping - * of column names to {@link DataObject} properties/relations. - * - * e.g. "BlogEntry" => "BlogEntryCsvBulkLoader" - * - * @var array - */ - public static $model_importers = null; - - /** - * Amount of results showing on a single page. - * - * @var int - */ - public static $page_length = 30; - - /** - * Class name of the form field used for the results list. Overloading this in subclasses - * can let you customise the results table field. - */ - protected $resultsTableClassName = 'TableListField'; - - /** - * Return {@link $this->resultsTableClassName} - */ - public function resultsTableClassName() { - return $this->resultsTableClassName; - } - - /** - * Initialize the model admin interface. Sets up embedded jquery libraries and requisite plugins. - * - * @todo remove reliance on urlParams - */ - public function init() { - parent::init(); - - // security check for valid models - if(isset($this->urlParams['Action']) && !in_array($this->urlParams['Action'], $this->getManagedModels())) { - //user_error('ModelAdmin::init(): Invalid Model class', E_USER_ERROR); - } - - Requirements::css(CMS_DIR . '/css/ModelAdmin.css'); // standard layout formatting for management UI - Requirements::css(CMS_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-livequery/jquery.livequery.js'); - Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/jquery-ui.js'); - Requirements::javascript(SAPPHIRE_DIR . '/javascript/jquery/jquery_improvements.js'); - Requirements::javascript(CMS_DIR . '/javascript/ModelAdmin.js'); - Requirements::javascript(CMS_DIR . '/javascript/ModelAdmin.History.js'); - } - - /** - * overwrite the static page_length of the admin panel, - * should be called in the project _config file. - */ - static function set_page_length($length){ - self::$page_length = $length; - } - - /** - * Return the static page_length of the admin, default as 30 - */ - static function get_page_length(){ - return self::$page_length; - } - - /** - * Return the class name of the collection controller - * - * @param string $model model name to get the controller for - * @return string the collection controller class - */ - function getCollectionControllerClass($model) { - $models = $this->getManagedModels(); - - if(isset($models[$model]['collection_controller'])) { - $class = $models[$model]['collection_controller']; - } else { - $class = $this->stat('collection_controller_class'); - } - - return $class; - } - - /** - * Return the class name of the record controller - * - * @param string $model model name to get the controller for - * @return string the record controller class - */ - function getRecordControllerClass($model) { - $models = $this->getManagedModels(); - - if(isset($models[$model]['record_controller'])) { - $class = $models[$model]['record_controller']; - } else { - $class = $this->stat('record_controller_class'); - } - - return $class; - } - - /** - * Add mappings for generic form constructors to automatically delegate to a scaffolded form object. - */ - function defineMethods() { - parent::defineMethods(); - foreach($this->getManagedModels() as $class => $options) { - if(is_numeric($class)) $class = $options; - $this->addWrapperMethod($class, 'bindModelController'); - self::$allowed_actions[] = $class; - } - } - - /** - * Base scaffolding method for returning a generic model instance. - */ - public function bindModelController($model, $request = null) { - $class = $this->getCollectionControllerClass($model); - return new $class($this, $model); - } - - /** - * This method can be overloaded to specify the UI by which the search class is chosen. - * - * It can create a tab strip or a dropdown. The dropdown is useful when there are a large number of classes. - * By default, it will show a tabs for 1-3 classes, and a dropdown for 4 or more classes. - * - * @return String: 'tabs' or 'dropdown' - */ - public function SearchClassSelector() { - return sizeof($this->getManagedModels()) > 3 ? 'dropdown' : 'tabs'; - } - - /** - * Returns managed models' create, search, and import forms - * @uses SearchContext - * @uses SearchFilter - * @return DataObjectSet of forms - */ - protected function getModelForms() { - $models = $this->getManagedModels(); - $forms = new DataObjectSet(); - - foreach($models as $class => $options) { - if(is_numeric($class)) $class = $options; - $forms->push(new ArrayData(array ( - 'Title' => (is_array($options) && isset($options['title'])) ? $options['title'] : singleton($class)->i18n_singular_name(), - 'ClassName' => $class, - 'Content' => $this->$class()->getModelSidebar() - ))); - } - - return $forms; - } - - /** - * @return array - */ - function getManagedModels() { - $models = $this->stat('managed_models'); - if(is_string($models)) { - $models = array($models); - } - if(!count($models)) { - user_error( - 'ModelAdmin::getManagedModels(): - You need to specify at least one DataObject subclass in public static $managed_models. - Make sure that this property is defined, and that its visibility is set to "public"', - E_USER_ERROR - ); - } - - return $models; - } - - /** - * Returns all importers defined in {@link self::$model_importers}. - * If none are defined, we fall back to {@link self::managed_models} - * with a default {@link CsvBulkLoader} class. In this case the column names of the first row - * in the CSV file are assumed to have direct mappings to properties on the object. - * - * @return array - */ - function getModelImporters() { - $importers = $this->stat('model_importers'); - - // fallback to all defined models if not explicitly defined - if(is_null($importers)) { - $models = $this->getManagedModels(); - foreach($models as $modelName => $options) { - if(is_numeric($modelName)) $modelName = $options; - $importers[$modelName] = 'CsvBulkLoader'; - } - } - - return $importers; - } - -} - -/** - * Handles a managed model class and provides default collection filtering behavior. - * - * @package cms - * @subpackage core - */ -class ModelAdmin_CollectionController extends Controller { - public $parentController; - protected $modelClass; - - public $showImportForm = null; - - static $url_handlers = array( - '$Action' => 'handleActionOrID' - ); - - function __construct($parent, $model) { - $this->parentController = $parent; - $this->modelClass = $model; - - parent::__construct(); - } - - /** - * Appends the model class to the URL. - * - * @param string $action - * @return string - */ - function Link($action = null) { - return $this->parentController->Link(Controller::join_links($this->modelClass, $action)); - } - - /** - * Return the class name of the model being managed. - * - * @return unknown - */ - function getModelClass() { - return $this->modelClass; - } - - /** - * Delegate to different control flow, depending on whether the - * URL parameter is a number (record id) or string (action). - * - * @param unknown_type $request - * @return unknown - */ - function handleActionOrID($request) { - if (is_numeric($request->param('Action'))) { - return $this->handleID($request); - } else { - return $this->handleAction($request); - } - } - - /** - * Delegate to the RecordController if a valid numeric ID appears in the URL - * segment. - * - * @param SS_HTTPRequest $request - * @return RecordController - */ - public function handleID($request) { - $class = $this->parentController->getRecordControllerClass($this->getModelClass()); - return new $class($this, $request); - } - - // ----------------------------------------------------------------------------------------------------------------- - - /** - * Get a combination of the Search, Import and Create forms that can be inserted into a {@link ModelAdmin} sidebar. - * - * @return string - */ - public function getModelSidebar() { - return $this->renderWith('ModelSidebar'); - } - - /** - * Get a search form for a single {@link DataObject} subclass. - * - * @return Form - */ - public function SearchForm() { - $context = singleton($this->modelClass)->getDefaultSearchContext(); - $fields = $context->getSearchFields(); - $columnSelectionField = $this->ColumnSelectionField(); - $fields->push($columnSelectionField); - $validator = new RequiredFields(); - $validator->setJavascriptValidationHandler('none'); - - $form = new Form($this, "SearchForm", - $fields, - new FieldSet( - new FormAction('search', _t('MemberTableField.SEARCH', 'Search')), - $clearAction = new ResetFormAction('clearsearch', _t('ModelAdmin.CLEAR_SEARCH','Clear Search')) - ), - $validator - ); - //$form->setFormAction(Controller::join_links($this->Link(), "search")); - $form->setFormMethod('get'); - $form->setHTMLID("Form_SearchForm_" . $this->modelClass); - $form->disableSecurityToken(); - $clearAction->useButtonTag = true; - $clearAction->addExtraClass('minorAction'); - - return $form; - } - - /** - * Create a form that consists of one button - * that directs to a give model's Add form - */ - public function CreateForm() { - $modelName = $this->modelClass; - - if($this->hasMethod('alternatePermissionCheck')) { - if(!$this->alternatePermissionCheck()) return false; - } else { - if(!singleton($modelName)->canCreate(Member::currentUser())) return false; - } - - $buttonLabel = sprintf(_t('ModelAdmin.CREATEBUTTON', "Create '%s'", PR_MEDIUM, "Create a new instance from a model class"), singleton($modelName)->i18n_singular_name()); - - $form = new Form($this, "CreateForm", - new FieldSet(), - new FieldSet($createButton = new FormAction('add', $buttonLabel)), - $validator = new RequiredFields() - ); - - $createButton->dontEscape = true; - $validator->setJavascriptValidationHandler('none'); - $form->setHTMLID("Form_CreateForm_" . $this->modelClass); - return $form; - } - - /** - * Checks if a CSV import form should be generated by a className criteria or in general for ModelAdmin. - */ - function showImportForm() { - if($this->showImportForm === null) return $this->parentController->showImportForm; - else return $this->showImportForm; - } - - /** - * Generate a CSV import form for a single {@link DataObject} subclass. - * - * @return Form - */ - public function ImportForm() { - $modelName = $this->modelClass; - // check if a import form should be generated - if(!$this->showImportForm() || (is_array($this->showImportForm()) && !in_array($modelName,$this->showImportForm()))) return false; - $importers = $this->parentController->getModelImporters(); - if(!$importers || !isset($importers[$modelName])) return false; - - if(!singleton($modelName)->canCreate(Member::currentUser())) return false; - - $fields = new FieldSet( - new HiddenField('ClassName', _t('ModelAdmin.CLASSTYPE'), $modelName), - new FileField('_CsvFile', false) - ); - - // get HTML specification for each import (column names etc.) - $importerClass = $importers[$modelName]; - $importer = new $importerClass($modelName); - $spec = $importer->getImportSpec(); - $specFields = new DataObjectSet(); - foreach($spec['fields'] as $name => $desc) { - $specFields->push(new ArrayData(array('Name' => $name, 'Description' => $desc))); - } - $specRelations = new DataObjectSet(); - foreach($spec['relations'] as $name => $desc) { - $specRelations->push(new ArrayData(array('Name' => $name, 'Description' => $desc))); - } - $specHTML = $this->customise(array( - 'ModelName' => Convert::raw2att($modelName), - 'Fields' => $specFields, - 'Relations' => $specRelations, - ))->renderWith('ModelAdmin_ImportSpec'); - - $fields->push(new LiteralField("SpecFor{$modelName}", $specHTML)); - $fields->push(new CheckboxField('EmptyBeforeImport', 'Clear Database before import', false)); - - $actions = new FieldSet( - new FormAction('import', _t('ModelAdmin.IMPORT', 'Import from CSV')) - ); - - $validator = new RequiredFields(); - $validator->setJavascriptValidationHandler('none'); - - $form = new Form( - $this, - "ImportForm", - $fields, - $actions, - $validator - ); - $form->setHTMLID("Form_ImportForm_" . $this->modelClass); - return $form; - } - - /** - * Imports the submitted CSV file based on specifications given in - * {@link self::model_importers}. - * Redirects back with a success/failure message. - * - * @todo Figure out ajax submission of files via jQuery.form plugin - * - * @param array $data - * @param Form $form - * @param SS_HTTPRequest $request - */ - function import($data, $form, $request) { - - $modelName = $data['ClassName']; - - if(!$this->showImportForm() || (is_array($this->showImportForm()) && !in_array($modelName,$this->showImportForm()))) return false; - $importers = $this->parentController->getModelImporters(); - $importerClass = $importers[$modelName]; - - $loader = new $importerClass($data['ClassName']); - - // File wasn't properly uploaded, show a reminder to the user - if( - empty($_FILES['_CsvFile']['tmp_name']) || - file_get_contents($_FILES['_CsvFile']['tmp_name']) == '' - ) { - $form->sessionMessage(_t('ModelAdmin.NOCSVFILE', 'Please browse for a CSV file to import'), 'good'); - $this->redirectBack(); - return false; - } - - if (!empty($data['EmptyBeforeImport']) && $data['EmptyBeforeImport']) { //clear database before import - $loader->deleteExistingRecords = true; - } - $results = $loader->load($_FILES['_CsvFile']['tmp_name']); - - $message = ''; - if($results->CreatedCount()) $message .= sprintf( - _t('ModelAdmin.IMPORTEDRECORDS', "Imported %s records."), - $results->CreatedCount() - ); - if($results->UpdatedCount()) $message .= sprintf( - _t('ModelAdmin.UPDATEDRECORDS', "Updated %s records."), - $results->UpdatedCount() - ); - if($results->DeletedCount()) $message .= sprintf( - _t('ModelAdmin.DELETEDRECORDS', "Deleted %s records."), - $results->DeletedCount() - ); - if(!$results->CreatedCount() && !$results->UpdatedCount()) $message .= _t('ModelAdmin.NOIMPORT', "Nothing to import"); - - $form->sessionMessage($message, 'good'); - $this->redirectBack(); - } - - - /** - * Return the columns available in the column selection field. - * Overload this to make other columns available - */ - public function columnsAvailable() { - return singleton($this->modelClass)->summaryFields(); - } - - /** - * Return the columns selected by default in the column selection field. - * Overload this to make other columns selected by default - */ - public function columnsSelectedByDefault() { - return array_keys(singleton($this->modelClass)->summaryFields()); - } - - /** - * Give the flexibilility to show variouse combination of columns in the search result table - */ - public function ColumnSelectionField() { - $model = singleton($this->modelClass); - $source = $this->columnsAvailable(); - - // select all fields by default - $value = $this->columnsSelectedByDefault(); - - // Reorder the source so that you read items down the column and then across - $columnisedSource = array(); - $keys = array_keys($source); - $midPoint = ceil(sizeof($source)/2); - for($i=0;$i<$midPoint;$i++) { - $key1 = $keys[$i]; - $columnisedSource[$key1] = $model->fieldLabel($source[$key1]); - // If there are an odd number of items, the last item will be unset - if(isset($keys[$i+$midPoint])) { - $key2 = $keys[$i+$midPoint]; - $columnisedSource[$key2] = $model->fieldLabel($source[$key2]); - } - } - - $checkboxes = new CheckboxSetField("ResultAssembly", false, $columnisedSource, $value); - - $field = new CompositeField( - new LiteralField( - "ToggleResultAssemblyLink", - sprintf("%s", - _t('ModelAdmin.CHOOSE_COLUMNS', 'Select result columns...') - ) - ), - $checkboxesBlock = new CompositeField( - $checkboxes, - new LiteralField("ClearDiv", "
    "), - new LiteralField( - "TickAllAssemblyLink", - sprintf( - "%s", - _t('ModelAdmin.SELECTALL', 'select all') - ) - ), - new LiteralField( - "UntickAllAssemblyLink", - sprintf( - "%s", - _t('ModelAdmin.SELECTNONE', 'select none') - ) - ) - ) - ); - - $field->addExtraClass("ResultAssemblyBlock"); - $checkboxesBlock->addExtraClass("hidden"); - return $field; - } - - /** - * Action to render a data object collection, using the model context to provide filters - * and paging. - * - * @return string - */ - function search($request, $form) { - // Get the results form to be rendered - $resultsForm = $this->ResultsForm(array_merge($form->getData(), $request)); - // Before rendering, let's get the total number of results returned - $tableField = $resultsForm->Fields()->dataFieldByName($this->modelClass); - $tableField->addExtraClass('resultsTable'); - $numResults = $tableField->TotalCount(); - - if($numResults) { - $msg = sprintf( - _t('ModelAdmin.FOUNDRESULTS',"Your search found %s matching items"), - $numResults - ); - } else { - $msg = _t('ModelAdmin.NORESULTS',"Your search didn't return any matching items"); - } - return new SS_HTTPResponse( - $resultsForm->formHtmlContent(), - 200, - $msg - ); - } - - /** - * Gets the search query generated on the SearchContext from - * {@link DataObject::getDefaultSearchContext()}, - * and the current GET parameters on the request. - * - * @return SQLQuery - */ - function getSearchQuery($searchCriteria) { - $context = singleton($this->modelClass)->getDefaultSearchContext(); - return $context->getQuery($searchCriteria); - } - - /** - * Returns all columns used for tabular search results display. - * Defaults to all fields specified in {@link DataObject->summaryFields()}. - * - * @param array $searchCriteria Limit fields by populating the 'ResultsAssembly' key - * @param boolean $selectedOnly Limit by 'ResultsAssempty - */ - function getResultColumns($searchCriteria, $selectedOnly = true) { - $model = singleton($this->modelClass); - - $summaryFields = $this->columnsAvailable(); - - if($selectedOnly && isset($searchCriteria['ResultAssembly'])) { - $resultAssembly = $searchCriteria['ResultAssembly']; - if(!is_array($resultAssembly)) { - $explodedAssembly = split(' *, *', $resultAssembly); - $resultAssembly = array(); - foreach($explodedAssembly as $item) $resultAssembly[$item] = true; - } - return array_intersect_key($summaryFields, $resultAssembly); - } else { - return $summaryFields; - } - } - - /** - * Creates and returns the result table field for resultsForm. - * Uses {@link resultsTableClassName()} to initialise the formfield. - * Method is called from {@link ResultsForm}. - * - * @param array $searchCriteria passed through from ResultsForm - * - * @return TableListField - */ - function getResultsTable($searchCriteria) { - - $summaryFields = $this->getResultColumns($searchCriteria); - - $className = $this->parentController->resultsTableClassName(); - $tf = new $className( - $this->modelClass, - $this->modelClass, - $summaryFields - ); - - $tf->setCustomQuery($this->getSearchQuery($searchCriteria)); - $tf->setPageSize($this->parentController->stat('page_length')); - $tf->setShowPagination(true); - // @todo Remove records that can't be viewed by the current user - $tf->setPermissions(array_merge(array('view','export'), TableListField::permissions_for_object($this->modelClass))); - - // csv export settings (select all columns regardless of user checkbox settings in 'ResultsAssembly') - $exportFields = $this->getResultColumns($searchCriteria, false); - $tf->setFieldListCsv($exportFields); - - $url = 'Link() . '/$ID/edit\">$value'; - if(count($summaryFields)) { - $tf->setFieldFormatting(array_combine( - array_keys($summaryFields), - array_fill(0,count($summaryFields), $url) - )); - } - - return $tf; - } - - /** - * Shows results from the "search" action in a TableListField. - * - * @uses getResultsTable() - * - * @return Form - */ - function ResultsForm($searchCriteria) { - - if($searchCriteria instanceof SS_HTTPRequest) $searchCriteria = $searchCriteria->getVars(); - - $tf = $this->getResultsTable($searchCriteria); - - // implemented as a form to enable further actions on the resultset - // (serverside sorting, export as CSV, etc) - $form = new Form( - $this, - 'ResultsForm', - new FieldSet( - new TabSet('Root', - new Tab('SearchResults', - _t('ModelAdmin.SEARCHRESULTS','Search Results'), - $tf - ) - ) - ), - new FieldSet() - ); - - // Include the search criteria on the results form URL, but not dodgy variables like those below - $filteredCriteria = $searchCriteria; - unset($filteredCriteria['ctf']); - unset($filteredCriteria['url']); - unset($filteredCriteria['action_search']); - - $form->setFormAction($this->Link() . '/ResultsForm?' . http_build_query($filteredCriteria)); - return $form; - } - - - ///////////////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Create a new model record. - * - * @param unknown_type $request - * @return unknown - */ - function add($request) { - return new SS_HTTPResponse( - $this->AddForm()->formHtmlContent(), - 200, - sprintf( - _t('ModelAdmin.ADDFORM', "Fill out this form to add a %s to the database."), - $this->modelClass - ) - ); - } - - /** - * Returns a form suitable for adding a new model, falling back on the default edit form. - * - * Caution: The add-form shows a DataObject's {@link DataObject->getCMSFields()} method on a record - * that doesn't exist in the database yet, hence has no ID. This means the {@link DataObject->getCMSFields()} - * implementation has to ensure that no fields are added which would rely on a - * record ID being present, e.g. {@link HasManyComplexTableField}. - * - * Example: - * - * function getCMSFields() { - * $fields = parent::getCMSFields(); - * if($this->exists()) { - * $ctf = new HasManyComplexTableField($this, 'MyRelations', 'MyRelation'); - * $fields->addFieldToTab('Root.Main', $ctf); - * } - * return $fields; - * } - * - * - * @return Form - */ - public function AddForm() { - $newRecord = new $this->modelClass(); - - if($newRecord->canCreate()){ - if($newRecord->hasMethod('getCMSAddFormFields')) { - $fields = $newRecord->getCMSAddFormFields(); - } else { - $fields = $newRecord->getCMSFields(); - } - - $validator = ($newRecord->hasMethod('getCMSValidator')) ? $newRecord->getCMSValidator() : null; - if(!$validator) $validator = new RequiredFields(); - $validator->setJavascriptValidationHandler('none'); - - $actions = new FieldSet ( - new FormAction("doCreate", _t('ModelAdmin.ADDBUTTON', "Add")) - ); - - $form = new Form($this, "AddForm", $fields, $actions, $validator); - $form->loadDataFrom($newRecord); - - return $form; - } - } - - function doCreate($data, $form, $request) { - $className = $this->getModelClass(); - $model = new $className(); - // We write before saveInto, since this will let us save has-many and many-many relationships :-) - $model->write(); - $form->saveInto($model); - $model->write(); - - if($this->isAjax()) { - $class = $this->parentController->getRecordControllerClass($this->getModelClass()); - $recordController = new $class($this, $request, $model->ID); - return new SS_HTTPResponse( - $recordController->EditForm()->formHtmlContent(), - 200, - sprintf( - _t('ModelAdmin.LOADEDFOREDITING', "Loaded '%s' for editing."), - $model->Title - ) - ); - } else { - Director::redirect(Controller::join_links($this->Link(), $model->ID , 'edit')); - } - } -} - -/** - * Handles operations on a single record from a managed model. - * - * @package cms - * @subpackage core - * @todo change the parent controller varname to indicate the model scaffolding functionality in ModelAdmin - */ -class ModelAdmin_RecordController extends Controller { - protected $parentController; - protected $currentRecord; - - static $allowed_actions = array('edit', 'view', 'EditForm', 'ViewForm'); - - function __construct($parentController, $request, $recordID = null) { - $this->parentController = $parentController; - $modelName = $parentController->getModelClass(); - $recordID = ($recordID) ? $recordID : $request->param('Action'); - $this->currentRecord = DataObject::get_by_id($modelName, $recordID); - - parent::__construct(); - } - - /** - * Link fragment - appends the current record ID to the URL. - */ - public function Link($action = null) { - return $this->parentController->Link(Controller::join_links($this->currentRecord->ID, $action)); - } - - ///////////////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Edit action - shows a form for editing this record - */ - function edit($request) { - if ($this->currentRecord) { - if($this->isAjax()) { - $this->response->setBody($this->EditForm()->formHtmlContent()); - $this->response->setStatusCode( - 200, - sprintf( - _t('ModelAdmin.LOADEDFOREDITING', "Loaded '%s' for editing."), - $this->currentRecord->Title - ) - ); - return $this->response; - } else { - // This is really quite ugly; to fix will require a change in the way that customise() works. :-( - return $this->parentController->parentController->customise(array( - 'Right' => $this->parentController->parentController->customise(array( - 'EditForm' => $this->EditForm() - ))->renderWith(array("{$this->class}_right",'LeftAndMain_right')) - ))->renderWith(array('ModelAdmin','LeftAndMain')); - } - } else { - return _t('ModelAdmin.ITEMNOTFOUND', "I can't find that item"); - } - } - - /** - * Returns a form for editing the attached model - */ - public function EditForm() { - $fields = $this->currentRecord->getCMSFields(); - $fields->push(new HiddenField("ID")); - - $validator = ($this->currentRecord->hasMethod('getCMSValidator')) ? $this->currentRecord->getCMSValidator() : new RequiredFields(); - $validator->setJavascriptValidationHandler('none'); - - $actions = $this->currentRecord->getCMSActions(); - if($this->currentRecord->canEdit(Member::currentUser())){ - if(!$actions->fieldByName('action_doSave') && !$actions->fieldByName('action_save')) { - $actions->push(new FormAction("doSave", _t('ModelAdmin.SAVE', "Save"))); - } - }else{ - $fields = $fields->makeReadonly(); - } - - if($this->currentRecord->canDelete(Member::currentUser())) { - if(!$actions->fieldByName('action_doDelete')) { - $actions->insertFirst($deleteAction = new FormAction('doDelete', _t('ModelAdmin.DELETE', 'Delete'))); - } - $deleteAction->addExtraClass('delete'); - } - - $form = new Form($this, "EditForm", $fields, $actions, $validator); - $form->loadDataFrom($this->currentRecord); - - return $form; - } - - /** - * Postback action to save a record - * - * @param array $data - * @param Form $form - * @param SS_HTTPRequest $request - * @return mixed - */ - function doSave($data, $form, $request) { - $form->saveInto($this->currentRecord); - - try { - $this->currentRecord->write(); - } catch(ValidationException $e) { - $form->sessionMessage($e->getResult()->message(), 'bad'); - } - - - // Behaviour switched on . - if($this->parentController->isAjax()) { - return $this->edit($request); - } else { - $this->parentController->redirectBack(); - } - } - - /** - * Delete the current record - */ - public function doDelete($data, $form, $request) { - if($this->currentRecord->canDelete(Member::currentUser())) { - $this->currentRecord->delete(); - Director::redirect($this->parentController->Link('SearchForm?action=search')); - } else { - $this->parentController->redirectBack(); - } - return; - } - - ///////////////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Renders the record view template. - * - * @param SS_HTTPRequest $request - * @return mixed - */ - function view($request) { - if($this->currentRecord) { - $form = $this->ViewForm(); - return $form->formHtmlContent(); - } else { - return _t('ModelAdmin.ITEMNOTFOUND'); - } - } - - /** - * Returns a form for viewing the attached model - * - * @return Form - */ - public function ViewForm() { - $fields = $this->currentRecord->getCMSFields(); - $form = new Form($this, "EditForm", $fields, new FieldSet()); - $form->loadDataFrom($this->currentRecord); - $form->makeReadonly(); - return $form; - } - - ///////////////////////////////////////////////////////////////////////////////////////////////////////// - - function index() { - Director::redirect(Controller::join_links($this->Link(), 'edit')); - } - - function getCurrentRecord(){ - return $this->currentRecord; - } - -} - -?> \ No newline at end of file diff --git a/code/SecurityAdmin.php b/code/SecurityAdmin.php deleted file mode 100644 index 25bf9ab7..00000000 --- a/code/SecurityAdmin.php +++ /dev/null @@ -1,372 +0,0 @@ -currentPageID(); - $form = parent::getEditForm($id); - - // TODO Duplicate record fetching (see parent implementation) - $record = $this->getRecord($id); - if($record && !$record->canView()) return Security::permissionFailure($this); - - if($id && is_numeric($id)) { - $form = parent::getEditForm($id); - if(!$form) return false; - - $fields = $form->Fields(); - if($fields->hasTabSet() && $record->canEdit()) { - $fields->findOrMakeTab('Root.Import',_t('Group.IMPORTTABTITLE', 'Import')); - $fields->addFieldToTab('Root.Import', - new LiteralField( - 'MemberImportFormIframe', - sprintf( - '', - $this->Link('memberimport') - ) - ) - ); - if(Permission::check('APPLY_ROLES')) { - $fields->addFieldToTab( - 'Root.Roles', - new LiteralField( - 'RolesAddEditLink', - sprintf( - '

    %s

    ', - $this->Link('show/root'), - // TODO This should include #Root_Roles to switch directly to the tab, - // but tabstrip.js doesn't display tabs when directly adressed through a URL pragma - _t('Group.RolesAddEditLink', 'Add/edit roles') - ) - ) - ); - } - - $form->Actions()->insertBefore( - $actionAddMember = new FormAction('addmember',_t('SecurityAdmin.ADDMEMBER','Add Member')), - 'action_save' - ); - $actionAddMember->setForm($form); - - // Filter permissions - $permissionField = $form->Fields()->dataFieldByName('Permissions'); - if($permissionField) $permissionField->setHiddenPermissions(self::$hidden_permissions); - } - - $this->extend('updateEditForm', $form); - } else { - $form = $this->RootForm(); - } - - return $form; - } - - /** - * @return FieldSet - */ - function RootForm() { - $memberList = new MemberTableField( - $this, - "Members" - ); - // unset 'inlineadd' permission, we don't want inline addition - $memberList->setPermissions(array('edit', 'delete', 'add')); - $memberList->setRelationAutoSetting(false); - - $fields = new FieldSet( - new TabSet( - 'Root', - new Tab('Members', singleton('Member')->i18n_plural_name(), - $memberList, - new LiteralField('MembersCautionText', - sprintf('

    %s

    ', - _t( - 'SecurityAdmin.MemberListCaution', - 'Caution: Removing members from this list will remove them from all groups and the database' - ) - ) - ) - ), - new Tab('Import', _t('SecurityAdmin.TABIMPORT', 'Import'), - new LiteralField( - 'GroupImportFormIframe', - sprintf( - '', - $this->Link('groupimport') - ) - ) - ) - ), - // necessary for tree node selection in LeftAndMain.EditForm.js - new HiddenField('ID', false, 0) - ); - - // Add roles editing interface - if(Permission::check('APPLY_ROLES')) { - $rolesCTF = new ComplexTableField( - $this, - 'Roles', - 'PermissionRole' - ); - $rolesCTF->setPermissions(array('add', 'edit', 'delete')); - - $rolesTab = $fields->findOrMakeTab('Root.Roles', _t('SecurityAdmin.TABROLES', 'Roles')); - $rolesTab->push(new LiteralField( - 'RolesDescription', - '' - )); - $rolesTab->push($rolesCTF); - } - - $actions = new FieldSet( - new FormAction('addmember',_t('SecurityAdmin.ADDMEMBER','Add Member')) - ); - - $this->extend('updateRootFormFields', $fields, $actions); - - $form = new Form( - $this, - 'EditForm', - $fields, - $actions - ); - - return $form; - } - - public function memberimport() { - Requirements::clear(); - Requirements::css(SAPPHIRE_DIR . '/css/Form.css'); - Requirements::css(CMS_DIR . '/css/typography.css'); - Requirements::css(CMS_DIR . '/css/cms_right.css'); - Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js'); - Requirements::css(CMS_DIR . '/css/MemberImportForm.css'); - Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-entwine/dist/jquery.entwine-dist.js'); - Requirements::javascript(CMS_DIR . '/javascript/MemberImportForm.js'); - - return $this->renderWith('BlankPage', array( - 'Form' => $this->MemberImportForm() - )); - } - - /** - * @see SecurityAdmin_MemberImportForm - * - * @return Form - */ - public function MemberImportForm() { - $group = $this->currentPage(); - $form = new MemberImportForm( - $this, - 'MemberImportForm' - ); - $form->setGroup($group); - - return $form; - } - - public function groupimport() { - Requirements::clear(); - Requirements::css(SAPPHIRE_DIR . '/css/Form.css'); - Requirements::css(CMS_DIR . '/css/typography.css'); - Requirements::css(CMS_DIR . '/css/cms_right.css'); - Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js'); - Requirements::css(CMS_DIR . '/css/MemberImportForm.css'); - Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-entwine/dist/jquery.entwine-dist.js'); - Requirements::javascript(CMS_DIR . '/javascript/MemberImportForm.js'); - - return $this->renderWith('BlankPage', array( - 'Form' => $this->GroupImportForm() - )); - } - - /** - * @see SecurityAdmin_MemberImportForm - * - * @return Form - */ - public function GroupImportForm() { - $form = new GroupImportForm( - $this, - 'GroupImportForm' - ); - - return $form; - } - - public function AddRecordForm() { - $m = Object::create('MemberTableField', - $this, - "Members", - $this->currentPageID() - ); - return $m->AddRecordForm(); - } - - /** - * Ajax autocompletion - */ - public function autocomplete() { - $fieldName = $this->urlParams['ID']; - $fieldVal = $_REQUEST[$fieldName]; - $result = ''; - $uidField = Member::get_unique_identifier_field(); - - // Make sure we only autocomplete on keys that actually exist, and that we don't autocomplete on password - if(!singleton($this->stat('subitem_class'))->hasDatabaseField($fieldName) || $fieldName == 'Password') return; - - $matches = DataObject::get($this->stat('subitem_class'),"\"$fieldName\" LIKE '" . Convert::raw2sql($fieldVal) . "%'"); - if($matches) { - $result .= "
      "; - foreach($matches as $match) { - // If the current user doesnt have permissions on the target user, - // he's not allowed to add it to a group either: Don't include it in the suggestions. - if(!$match->canView() || !$match->canEdit()) continue; - - $data = array(); - foreach($match->summaryFields() as $k => $v) { - $data[$k] = $match->$k; - } - $result .= sprintf( - '
    • %s (%s)
    • ', - Convert::raw2att(Convert::raw2json($data)), - $match->$fieldName, - implode(',', array_values($data)) - ); - } - $result .= "
    "; - return $result; - } - } - - function getCMSTreeTitle() { - return _t('SecurityAdmin.SGROUPS', 'Security Groups'); - } - - public function EditedMember() { - if(Session::get('currentMember')) return DataObject::get_by_id('Member', (int) Session::get('currentMember')); - } - - function providePermissions() { - return array( - 'EDIT_PERMISSIONS' => array( - 'name' => _t('SecurityAdmin.EDITPERMISSIONS', 'Manage permissions for groups'), - 'category' => _t('Permissions.PERMISSIONS_CATEGORY', 'Roles and access permissions'), - 'help' => _t('SecurityAdmin.EDITPERMISSIONS_HELP', 'Ability to edit Permissions and IP Addresses for a group. Requires the "Access to \'Security\' section" permission.'), - 'sort' => 0 - ), - 'APPLY_ROLES' => array( - 'name' => _t('SecurityAdmin.APPLY_ROLES', 'Apply roles to groups'), - 'category' => _t('Permissions.PERMISSIONS_CATEGORY', 'Roles and access permissions'), - 'help' => _t('SecurityAdmin.APPLY_ROLES_HELP', 'Ability to edit the roles assigned to a group. Requires the "Access to \'Security\' section" permission.'), - 'sort' => 0 - ) - ); - } - - /** - * The permissions represented in the $codes will not appearing in the form - * containing {@link PermissionCheckboxSetField} so as not to be checked / unchecked. - * - * @param $codes String|Array - */ - static function add_hidden_permission($codes){ - if(is_string($codes)) $codes = array($codes); - self::$hidden_permissions = array_merge(self::$hidden_permissions, $codes); - } - - /** - * @param $codes String|Array - */ - static function remove_hidden_permission($codes){ - if(is_string($codes)) $codes = array($codes); - self::$hidden_permissions = array_diff(self::$hidden_permissions, $codes); - } - - /** - * @return Array - */ - static function get_hidden_permissions(){ - return self::$hidden_permissions; - } - - /** - * Clear all permissions previously hidden with {@link add_hidden_permission} - */ - static function clear_hidden_permissions(){ - self::$hidden_permissions = array(); - } -} - -/** - * Delete multiple {@link Group} records. Usually used through the {@link SecurityAdmin} interface. - * - * @package cms - * @subpackage batchactions - */ -class SecurityAdmin_DeleteBatchAction extends CMSBatchAction { - function getActionTitle() { - return _t('AssetAdmin_DeleteBatchAction.TITLE', 'Delete groups'); - } - - function run(DataObjectSet $records) { - $status = array( - 'modified'=>array(), - 'deleted'=>array() - ); - - foreach($records as $record) { - // TODO Provide better feedback if permission was denied - if(!$record->canDelete()) continue; - - $id = $record->ID; - $record->delete(); - $status['deleted'][$id] = array(); - $record->destroy(); - unset($record); - } - - return Convert::raw2json($status); - } -} -?> diff --git a/css/Image_iframe.css b/css/Image_iframe.css deleted file mode 100755 index 466e41ed..00000000 --- a/css/Image_iframe.css +++ /dev/null @@ -1,81 +0,0 @@ -@import url("typography.css"); - -html,body { - padding: 0; - margin: 0; - border-style: none; - height: 100%; - overflow: hidden; -} - -form { - margin: 0; padding: 0; -} - -h2 { - margin: 0; - font-size: 1.4em; -} - -/** - * Selection Groups - */ -.SelectionGroup { - padding: 0; - margin: 10px 0 0 0; -} -.SelectionGroup li { - list-style-type: none; - margin: 0 0 4px; -} -.SelectionGroup li label { - font-size: 11px; -} -.SelectionGroup li input.selector { - width: 20px; - margin-top: 0; -} - - -.SelectionGroup li div.field { - display: none; -} -.SelectionGroup li.selected div.field { - display: block; - margin-left: 30px; - margin-bottom: 1em; - margin-top: 4px; -} -.mainblock .SelectionGroup li.selected div.field { - margin-left: 27px; - margin-bottom: 4px; -} - -.SelectionGroup li.selected label.selector { - font-weight: bold; -} - -.Actions { - text-align: right; - margin: 0; - position: absolute; - right: 5px; - bottom: 5px; -} - -.mainblock { - float: left; - border: 1px #CCC solid; - padding: 5px; - margin-right: 5px; - height: 140px; - position: relative; -} - -.mainblock form fieldset { - border: none; -} - -.mainblock form div.Actions input { - font-size: 11px; -} \ No newline at end of file diff --git a/css/LeftAndMain_printable.css b/css/LeftAndMain_printable.css deleted file mode 100755 index 3b228738..00000000 --- a/css/LeftAndMain_printable.css +++ /dev/null @@ -1,36 +0,0 @@ -* { - font-family: Helvetica, Verdana, Geneva, Arial, Helvetica, sans-serif; -} - -h3 { - margin-top: 2em; -} - -form .field { - margin-bottom: 1em; -} - -form label.left { - float: left; - width: 15em; - font-weight: bold; -} -form div.field { - /*margin-left: 10em;*/ -} - -form .inlineformaction, -form .inlineformaction_readonly, -form .action, -form .action_readonly { - display: none !important; -} - -.TableListField table.data th { - background: #eee; -} - -.TableListField table.data { - width: 100%; - border: 1px #aaa solid; -} \ No newline at end of file diff --git a/css/MemberImportForm.css b/css/MemberImportForm.css deleted file mode 100644 index 16927ddf..00000000 --- a/css/MemberImportForm.css +++ /dev/null @@ -1,25 +0,0 @@ -* { - font-size:12px; -} - -body { - margin: 0; - padding: 0; -} - -form.import-form { - margin: 0; -} - - form.import-form #CsvFile .middleColumn { - background: none !important; - } - - form.import-form .advanced h4 { - margin-bottom: .5em; - } - - form.import-form .Actions { - text-align: left; - border: 0; - } \ No newline at end of file diff --git a/css/ModelAdmin.css b/css/ModelAdmin.css deleted file mode 100644 index 2e023666..00000000 --- a/css/ModelAdmin.css +++ /dev/null @@ -1,173 +0,0 @@ -.clear{ - display: block; - clear: both; -} - -body.ModelAdmin .ui-layout-west { - width: 280px; -} - -body.ModelAdmin #Form_AddForm fieldset { - border: none; -} - -body.ModelAdmin #Form_AddForm #ClassName { - margin: 0; - padding: auto; - width: auto; -} - -body.ModelAdmin #Form_AddForm #ClassName label { - display: none; -} - -body.ModelAdmin #SearchForm_holder, -body.ModelAdmin #AddForm_holder { - padding: 1em; -} - -body.ModelAdmin #SearchForm_holder { - overflow: auto; - margin-bottom:1em; -} - -body.ModelAdmin #ModelClassSelector { - border-bottom: 1px #AAA solid; - padding-bottom: 0.5em; -} - - -body.ModelAdmin #SearchForm_holder div.ResultAssemblyBlock{ - margin-bottom: 1em; -} - -body.ModelAdmin #SearchForm_holder form div.field.hidden{ - display: none; -} - -body.ModelAdmin #SearchForm_holder form div#ResultAssembly{ - margin: 0; - display: block; - clear: both; -} - body.ModelAdmin #SearchForm_holder form div#ResultAssembly label{ - width: auto; - clear: both; - margin: 0px; - } - - body.ModelAdmin #SearchForm_holder form div#ResultAssembly ul.optionset li{ - float: left; - width: 45%; - } - -body.ModelAdmin .resultsTable { - overflow:auto; -} -body.ModelAdmin .resultsTable tbody td { - cursor: pointer; -} - -body.ModelAdmin #right{ - overflow:hidden !important; - -} - -body.ModelAdmin #right table.results { - margin:6px; - padding:0; - border-spacing:0 2px; -} - -body.ModelAdmin #right table.results td { - font-size:12px; - font-weight:bold; - background-color:#fff; - padding:2px; -} - -body.ModelAdmin #right table.results td.over { - background-color:#bbb; - cursor:pointer; -} - -body.ModelAdmin #right table.results td.active { - background-color:#555; - color:#fff; -} - -body.ModelAdmin #right .tab { - overflow: auto; - padding-bottom: 20px; -} - -body.ModelAdmin #Form_ImportForm dt { - float: left; - width: 10em; - margin-right: 2em; -} - -body.ModelAdmin #statusMessage { - position: fixed !important; - position: absolute; - left: auto !important; - bottom: 35px !important; -} - -/* Form */ - -body.ModelAdmin .ui-layout-west form { - border-bottom: 2px solid #ddd; - padding-bottom: 10px; -} - -body.ModelAdmin .ui-layout-west #Form_ImportForm { - border-bottom: none; -} - -body.ModelAdmin .ui-layout-west form .Actions { - margin: 5px 0; -} - -body.ModelAdmin #right .Actions { - position:absolute; - bottom:4px; -} - -body.ModelAdmin #right .ajaxActions { - position:absolute; - bottom:4px; - height: 30px; -} - -body.ModelAdmin .ui-layout-west .tab { - padding: 7px; -} - -body.ModelAdmin .tab h3 { - margin: 10px 0 5px 0; - font-size: 12px; -} - -form .message { - font-size: 10px; - margin: .3em 0; - padding: 0.3em; -} - -body.ModelAdmin .historyNav { - background: #aaa; - padding: 0 10px; - overflow: auto; -} - body.ModelAdmin .historyNav a { - display: block; - color: white; - margin: 3px 0; - } - body.ModelAdmin .historyNav a.back { - float: left; - } - body.ModelAdmin .historyNav a.forward { - float: right; - } \ No newline at end of file diff --git a/css/SecurityAdmin.css b/css/SecurityAdmin.css deleted file mode 100644 index 90a30cea..00000000 --- a/css/SecurityAdmin.css +++ /dev/null @@ -1,14 +0,0 @@ -#Form_EditForm_Permissions .Arg, -#Form_EditForm_Permissions .Enabled { - width: 6em; -} - -#Form_EditForm_Permissions .Arg input { - text-align: right; -} - -/* Ensure there's enough room in the tab for TreeMultiSelectField expansion */ -#MemberTableField_Popup_DetailForm #Groups, -#MemberTableField_Popup_AddForm #Groups { - height: 250px; -} \ No newline at end of file diff --git a/css/cms_left.css b/css/cms_left.css deleted file mode 100644 index 67f5d618..00000000 --- a/css/cms_left.css +++ /dev/null @@ -1,414 +0,0 @@ -#left form.actionparams { - background: #eee; - padding: 0 5px 5px 5px; - _padding-left: 5px; - float: left; - width: 100%; -} - #left form#search_options { - padding-bottom: 8px; - } -#left form.actionparams input, -#left form.actionparams select { - padding: 1px; -} -#left form.actionparams select, -#ReportSelector_holder select { - width: 75%; -} - /* reset for a fieldtype that shouldn't have this width setting */ - #left form.actionparams .dropdownpopup select { - width: auto; - } - -#PageType, -#ReportSelector_holder select { - float: left; - margin-right: 4px; - padding: 1px; -} -#left form.actionparms input.action { - width: 5%; -} - -#Form_DeleteItemsForm p { - margin: 4px 8px 4px 0; - font-size: 11px; - color: #666; -} - -form.oneline fieldset { - width: 80%; - float: left; - margin-right: .5em; -} - -/** - * Selection Groups - */ -#left .SelectionGroup { - display: block; -} -#left .SelectionGroup li { - list-style-type: none; - clear: left; - border: 1px solid #ccc; -} -#left .SelectionGroup li input { - width: 20px; - float: left; -} -#left .SelectionGroup li div.field { - display: none; - font-size: 1em; -} -#left .SelectionGroup li label { - margin: 0 0 0 2px; - display: block; - height: 1.2em; - width: auto; - float: none; -} -#left .SelectionGroup li.selected div.field { - margin-left: 23px; - display: block; - margin-bottom: 1em; -} -#left div.field select { - width: 100%; -} -#left .SelectionGroup li.selected label.selector { - font-weight: bold; -} - -#publication_key { - border-bottom: 1px solid #ccc; - background: #eee; - padding: 3px; - font-weight: bold; -} - #publication_key ins, - #publication_key del, - #publication_key span { - margin: 0 5px; - font-weight: normal; - } - #publication_key span.notinmenu { - color: #77A; - } - /* Created on stage, never published */ - #publication_key ins { - color: orange; - text-decoration : none; - } - /* Deleted on stage */ - #publication_key del { - color: red; - } - /* Deleted on stage & live (when show deleted pages is active) */ - #publication_key del.deletedOnLive { - color: #700; - } - - #publication_key span.modified { - color: green; - text-decoration : none; - } - -#left { - background-color: #eee; -} - -#treepanes .ui-accordion-content { - padding: 0; -} -.ui-layout-pane-west { - /* OVERRIDE 'default styles' */ - padding: 0 !important; - overflow: hidden !important; -} - -#treepanes h2 { - cursor: pointer; -} - -#treepanes h2 img { - float:right; - margin-right: 2px; -} - -#treepanes form .field { - margin: 0; -} - -.listpane p { - display: block; - margin: 0px 4px 0px 4px; - padding: 3px 0px; - font-size: 10px; -} -.listpane ul { - margin: 0; - padding: 0; -} -.listpane ul li { - list-style-type: none; - font-size: 10px; - border-bottom: 1px #CCC dotted; - background-color: #FFF; -} -.listpane ul li.odd { - background-color: #EEE; -} - -.listpane ul li .extra { - font-style: italic; - color: #666; -} -.listpane div.unitBody { - overflow: auto; - width: 100%; - clear: left; -} - -.listpane li a { - display: block; - padding: 5px; -} - -.listpane input.action { - padding: 0; -} - -.listpane p.message { - padding: 5px; -} - - -#treepanes .pane_actions { - margin: 0px 4px 0px 4px; -} - -#treepanes .pane_actions a { - margin: 4px; - padding: 3px; - background-color: #EEE; - border: 1px #CCC solid; -} -#treepanes .pane_actions a.current { - background-color: #CCC; -} - - -#treepanes table { - border-collapse: collapse; - width: 100%; -} - -#treepanes table thead td { - background-color: #aaa; - color: #fff; - border-bottom: 2px #ccc solid; - padding: 0 3px; -} - -#treepanes table tbody tr { - cursor: pointer; -} - -#treepanes table tbody td { - font-size: 11px; - padding: 1px 3px; -} - -#treepanes table tbody tr.even td { - background-color: #EEE; -} -#treepanes table tbody tr.over td { - background-color: #FFFFBB; -} - -#treepanes table tbody tr.current td { - background-color: #BBBBFF; -} - -#treepanes #Versions tbody tr.internal { - color: #777; -} -#treepanes #Versions tbody tr.published { - color: black; -} - -.SideReport_RecentlyEdited li a:hover { - background-color: #FFFFBB; -} - -/* This applies to both the LHS context menu and the TinyMCE context menu */ -.contextMenu { - z-index: 1000; - background-color: white; - border: 1px #CCC solid; -} - -ul.contextMenu { - padding: 0; - margin: 0; -} -ul.contextMenu li { - list-style-type: none; - margin: 0; - padding: 0; -} -ul.contextMenu li a { - display: block; - font-family: Tahoma, Verdana, Arial, Helvetica; - font-size: 11px; - padding: 5px 10px 5px 5px; - color: black; - text-decoration: none; -} -ul.contextMenu li a:hover { - background-color: #B6BDD2; - text-decoration: none; -} - -/* Tree */ - -/* Added through jstree.js on DOM load, but we need it earlier in order to correctly display the uninitialized tree */ -.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; } - -ul.tree span.untranslated a:link, -ul.tree span.untranslated a:hover, -ul.tree span.untranslated a:visited { - color: #ccc -} - -#sitetree_ul li.disabled a { - color: #aaa; -} - -#Form_SearchTreeForm .field .middleColumn { - width: 60%; - float: left; - margin: 0; -} - -#Form_SearchTreeForm .field label { - margin-left: 0; - width: 28%; -} - -#Form_SearchTreeForm select.options { - clear: left; -} - -/* Change detection CSS */ -#right .compare ins, #right .compare ins * { - background-color: #99EE66; - text-decoration: none; -} - -#right .compare del, #right .compare del *{ - background-color: #FF9999; - text-decoration: line-through; -} - -#sitetree a.contents { - font-style: italic; -} - -#ShowDrafts { - float: none; -} - -.noBottomBorder { - border-bottom: none; -} - -.checkboxAboveTree { - border-top: 1px solid #cccccc; - padding: .5em; - overflow: hidden; - clear: left; -} - .checkboxAboveTree div { - clear: left; - } - .checkboxAboveTree input, #ShowChanged input, #ShowDrafts input { - float: left; - margin: 0 3px 0 0; - } - -#versions_actions { - float: left; - width: 100%; - border-bottom:1px solid #CCC; -} -#versions_actions .versionChoice { - float: left; - display: block; - margin: 0 0 5px 0; -} - - .versionChoice label { - display: block; - float: left; - margin-top: 3px; - _margin-top: 4px; - } - -#versions_actions input, #Remember input { - float: left; - display: block; - margin: 0 3px 0 0; -} - -#ReportSelector_holder { - background: #EEE; - margin: 0 !important; - padding: 3px 3px 7px; - border-bottom: 1px solid #CCC; -} -#versions_actions { - background: #EEE; - overflow: hidden; - margin: 0 !important; - padding: 4px 7px; -} -#Form_VersionsForm td.checkbox { - -} - -#Form_SideReportsForm select, -#Form_SideReportsForm .middlecolumn { - width: 80%; -} -/** - * i18n - */ -#LangSelector_holder { - float: none; - background-color: #EEEEEE; - border-bottom: 1px solid #CCCCCC; - border-top: 1px solid #CCCCCC; - padding: 3px 0 3px 7px; -} - #LangSelector_holder .Actions { - display: none; - } - #LangSelector_holder label.left { - margin-left: 0; - width: auto; - } \ No newline at end of file diff --git a/css/cms_right.css b/css/cms_right.css deleted file mode 100644 index e6e6b902..00000000 --- a/css/cms_right.css +++ /dev/null @@ -1,590 +0,0 @@ -/** - * General - */ -#right p/*, -#right .optionset*/ { - margin: 7px 0; -} - #right .optionset li { - margin: 8px 0; - } - -/** - * Forms - */ -.right form { - margin: 1em; - position: relative; -} - -/* legend tag shows up annoying spacing in IE6/7 */ -.right form legend { - display: none; -} - -.right form .field { - border: none; - margin: 0; -} - .right form .text, - .right form .field span.readonly, - .right form .field span.readonly i, - .right form div.TableListField table i { - font-size: 12px; - } - .right form .field span.readonly { - margin: 0; - width: auto; - } - .right form #URL .fieldgroup * { - font-size: 12px; - font-weight: bold; - } - .right form .fieldgroupField { - display: inline; - } - .right form #URL .fieldgroup { - display:block; - height: 16px; - background-color:#F7F7F7; - border:1px dotted #CCCCCC; - padding:3px; - } - .right form #URL .originallang_holder { - display: inline; - border: none; - padding: 0; - } - .right form #URL .fieldgroup input { - border: 1px solid #A7A7A7; - padding: 2px; - margin-top: -3px; - } - .right form #URL .fieldgroup span.readonly { - display: inline; - border: none; - padding: 0; - } - - .right form .parentTypeSelector { - margin-left: 0 !important; - } - .right form .parentTypeSelector label.left { - float: none !important; - margin-left: 0 !important; - width: auto !important; - } - - .right form .dropdown, - .right form .countrydropdown { - font-size: 12px; - } - .right form .dropdown select, - .right form .countrydropdown select { - font-size: 12px; - height: 19px; - } - .right form .dropdown select option, - .right form .countrydropdown select option { - font-size: 12px; - } - - .right form .field .middleColumn { - display: block; - background: #e9e9e9; - padding: 3px; - width: 97%; - } - .right form .field .middleColumn .middleColumn { - padding: 0; - } - .right form .field.image .middleColumn, - .right form .field.simpleimagefield_disabled .middleColumn { - padding-bottom:0; - } - - .right form .htmleditorfield span.readonly *, - #right form .htmleditorfield span.readonly * { - margin: 0; - border: 0; - padding: 0; - } - -.right form .checkbox input, -.right form .optionset input { - border: none; -} - -.right form .field label { - font-size: 11px; - color: #666; -} -.right form .field label.left { - float: none; - display: block; - margin-left: 0; - margin-bottom: 3px; - /*font-size: 11px;*/ - width: 96%; -} - -.time .timeIcon, -.datetime .timeIcon { - margin-left: 2px; -} - -.right form .date input.month, -.right form .date input.day, -.right form .date input.year { - width: auto; -} - -.right form .datetime .clear { - clear: both; -} - -.right form .datetime .date, -.right form .datetime .time { - float: left; - clear: none; - width: auto; -} - .right form .datetime .middleColumn .middleColumn { - width: auto; - } - .right form .datetime .date input, - .right form .datetime .time input { - width: auto; - } - -.right form textarea, -.right form input.text, -.right form textarea.htmleditor { - border: 1px solid #a7a7a7; - padding: 3px; - width: 99%; -} -.right form textarea { - font-size: 12px; -} - -.right form input.checkbox, -.right form .optionset input, -.right form .htmleditor select { - width: auto; - margin-right: 4px; -} - -.right form #Root_Access input { - float: left; -} - -.right form ul.optionset { - padding: 0; -} -.right form .optionset li { - list-style: none; -} -.right form .optionset li.cancel { - float: left; -} -.right form .optionset li.submit { - float: right; -} - -.right form h1 { - font-size: 20px; -} -.right form h2 { - font-size: 18px; - clear: both; -} -.right form h3 { - font-size: 16px; -} -.right form h4 { - font-size: 14px; -} -.right form h5 { - font-size: 12px; -} - -.right form .fieldgroup input, -.right form .fieldgroup select { - width: auto; -} - -.right form fieldset { - height: 100%; -} - -.right form .Actions { - text-align: right; - padding: 0.5em; - background: white; - border-top: 1px solid #aaa; -} - - .right form .Actions input { - padding: .5em; - font-size: 11px; - } - - .right form.loading { - margin-left: -1000em; - margin-right: 1000em; - } - -#SiteTreeFilterDate { - width: 68px; -} - -/** - * Tinymce - */ -.right form .mceEditor select.mceNativeListBox { - background: #fff; - padding-top: 2px; - width: 95px; /* Added width to stop sizes jumping and text wrapping */ - font-size: 11px; -} - .right form .mceEditor select.mceNativeListBox option { - padding: 0 2px 0 2px; - font-size: 11px; - } - .right form .mceEditor .mceFirst { - background: #f4f4f4; - } - .right form .mceEditor .mceToolbar table { - background: transparent; - } - .right form .mceEditor .mceButton { - padding: 1px; - } - .right form .mceEditor select.mceNativeListBox { - background: #fff; - } - .right form .mceEditor .mceFirst { - background: #fbfbfb url(../images/textures/mce_editor.gif) repeat-x bottom; - } - .right form .mceEditor .mceToolbar table { - background: transparent; - } - - .right form .mceEditor select.mceNativeListBox { - background: #fff; - } - -/** - * Non-tabbed scrolling area that can be used in place of tabs in LeftAndMAin - */ -.right #ScrollPanel { - background: #fff; - clear: left; - overflow: auto; - border: 1px solid #aaa; - position: relative; - top: 0; - padding: 10px; -} - -/** - * RHS Action Parameters boxes - */ -#right form.actionparams { - border: 1px solid #777; - background: #ccc; - position: absolute; - bottom: 32px; - right: 11px; - width: 300px; - z-index: 200; - padding: 10px; -} - - #right form.actionparams div.field { - margin-left: 6em; - } - - #right form.actionparams label.left { - width: 6em; - margin-left: -6em; - } - #right form.actionparams input, #right form.actionparams select, #right form.actionparams textarea { - width: 100%; - } - #right form.actionparams input.checkbox, #right form.actionparams .optionset input { - width: auto; - } - - #right form.actionparams p.label { - margin: 10px 0 0 0; - } - #right form.actionparams .actions { - margin: 10px 0 0 0; - text-align: right; - } - #right form.actionparams .actions input { - width: auto; - } - #right form.actionparams .sendingText { - position: absolute; - left: 0; - top: 0; - width: 100%; - padding: 10% 0 0 0; - height: 60%; - background: #ccc; - font-size: 25px; - font-weight: bold; - color: #fff; - text-align: center; - } - - #right form.actionparams div.TreeDropdownField { - width: 220px; - padding-bottom: 150px; - -} -#right form.actionparams div.TreeDropdownField .items { - width: 195px; -} -#right form.actionparams div.TreeDropdownField div.tree_holder { - width: 214px; -} - -/** - * Autocomplete - */ -.autocomplete { - margin-left: 1px; - background: white; - overflow: visible; -} - .autocomplete ul { - border: 1px solid #aaa; - background: #FFFFBB; - } - .autocomplete li { - list-style-type: none; - cursor: pointer; - font-size: 12px; - padding: 3px; - white-space: nowrap; - } - .autocomplete li.selected { - background: Highlight; - color: HighlightText; - } - .autocomplete .data { - display: none; - } - .autocomplete .informal { - font-style: italic; - } - - -/** - * Status Messages - */ -.notice-wrap { - position: absolute; - z-index: 500; - bottom: 4px; - left: 10px; - z-index: 9999; - width: auto; -} - -* html .notice-wrap { - position: absolute; -} - -.notice-item { - display: block; - position: relative; - width: auto; - background: #f4f3eb; - border: 1px solid #a9a67d; - padding: 2px 20px 2px 20px; - font-size: 14px; - font-weight: bold; - line-height: 1.5; - -moz-border-radius: 6px; - -webkit-border-radius: 6px; -} - -.notice-item-close { - position: absolute; - font-family: Arial; - font-size: 12px; - font-weight: bold; - right: 6px; - top: 6px; - cursor: pointer; -} - - .notice-wrap .good { - padding-left: 40px; - color: #060; - background: #E2F9E3 url(../images/alert-good.gif) 7px 50% no-repeat; - border: 1px solid #8dd38d; - } - .notice-wrap .bad { - padding-left: 40px; - color: #c80700; - background: #ffe9e9 url(../images/alert-bad.gif) 7px 50% no-repeat; - border: 1px solid #ff8e8e; - } - -/** - * TableField Subclasses - */ - -/* Filter box (for search/filter box above a table on Asset/MemberTableField) */ -.right div.filterBox { - width: inherit; - margin: 5px 0; - padding: 5px; - background: #EEE; - border: 1px solid #BBB; -} - .right div.filterBox .middleColumn { - background: none !important; - } - - .right form div.filterBox label { - display: block; - width: 5em; - float: left; - margin-left: 0; - line-height: 2.3; - font-weight: bold; - font-size: 12px; - } - - .right div.filterBox div.field { - margin-left: 0 !important; - padding: 0; - font-size: 12px; - display: inline; - } - - .right div.filterBox input, - .right div.filterBox select { - width: auto; - } - - -/** - * i18n - */ -.right form div.originallang_holder { - border: 1px dashed #aaa; - margin-bottom: 10px; - margin-left: 0 !important; - padding: 8px 0 6px 5px; -} - -.originallang, -#Form_EditForm_URLSegment_original { - color: #666; - font-style: italic; -} - -.right form .CompositeField .originallang label.left, -.right form div.originallang_holder label.left { - margin-left: 0 !important; - width: auto !important; - float: none !important; -} - -.right form div.CompositeField .originallang .middleColumn { - clear: left; -} - -.right form div.languageDropdown { - clear: right; - float: left; - padding-right: 4px; -} - -.right form .createTranslationButton .middleColumn { - background: none; -} - -#contentPanel h2 img { - float: right; - margin-right: 2px; - cursor: pointer; -} - -.right form .field.confirmedpassword.nolabel .middleColumn { - background-color: #fff; - padding: 0px; -} - - .right form .field.confirmedpassword.nolabel .middleColumn .middleColumn { - background-color: #e9e9e9; - padding: 3px; - } - -.autocompleteoptions { - background-color: white; -} - -/** - * Tabs - */ -.right form .ss-tabset { - /* Need this to stop tabs from disappearing sometimes */ - position: relative; -} - - .right form .ss-tabset .tab { - position: relative; - overflow-x: hidden; - } - -/** - * "Insert Image" forms etc. - */ -#Form_EditorToolbarImageForm .field, -#Form_EditorToolbarFlashForm .field, -#Form_EditorToolbarLinkForm .field { - 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) - */ -#Form_EditorToolbarImageForm .file, -#Form_EditorToolbarImageForm #UploadFormResponse { - display: none; -} - -.showUploadField { - font-size: 11px; - padding: 1px 3px; -} - .showUploadField a { - display: none; - } - - #UploadFormResponse { - padding: 6px 0; - font-size: 12px; - } - #UploadFormResponse.loading - .loading#UploadFormResponse { - background: url(../../cms/images/network-save.gif) no-repeat left center; - padding-left: 20px; - } \ No newline at end of file diff --git a/css/editor.css b/css/editor.css deleted file mode 100755 index 384a185e..00000000 --- a/css/editor.css +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Default Editor Styles for the CMS - */ - -.typography .mceItemFlash { - border: 1px dotted #f00; -} - -body.mceContentBody { - min-height: 200px; - font-size: 62.5%; -} - -body.mceContentBody a.ss-broken { - background-color: #FF7B71; - border: 1px red solid; - color: #fff; - padding: 1px; - text-decoration: underline; -} \ No newline at end of file diff --git a/css/layout.css b/css/layout.css deleted file mode 100644 index 08b353e7..00000000 --- a/css/layout.css +++ /dev/null @@ -1,713 +0,0 @@ -* { - margin: 0; - padding: 0; - list-style: none; - font-family: Arial, Helvetica, sans-serif; -} - -html { - overflow: hidden; -} - -body { - background: #ccdef3; - height: 100%; -} - -.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, #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-repeat: no-repeat; - background-color: transparent; - background-position: 50% 50%; -} - -.ss-loading-screen p { - width: 100%; - text-align: center; - position: absolute; - bottom: 80px; -} - -.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; - - -moz-box-shadow: 1px 1px 15px rgba(0,0,0, 0.1); - -webkit-box-shadow: 1px 1px 15px rgba(0,0,0, 0.1); - -o-box-shadow: 1px 1px 15px rgba(0,0,0, 0.1); - box-shadow: 1px 1px 15px rgba(0,0,0, 0.1); -} - -.ss-loading-screen .loading-animation { - display: none; - position: absolute; - left: 49%; - top: 75%; -} - -/* - * Default Layout Theme - * - * Created for jquery.layout - * - * Copyright (c) 2008 - * Fabrizio Balliano (http://www.fabrizioballiano.net) - * Kevin Dalman (http://allpro.net) - * - * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html) - * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses. - * - * Last Updated: 2009-03-04 - * NOTE: For best code readability, view this with a fixed-space font and tabs equal to 4-chars - */ - -/* -* PANES -*/ -.ui-layout-pane { /* all 'panes' */ - background: #FFF; - /*border: 1px solid #BBB;*/ - overflow: auto; -} - -/* Overflow is handled by tabsets inside the panel */ -.ui-layout-center, -.ui-layout-east { - overflow: hidden !important; -} - -.ui-layout-west { - width: 225px; -} - -.ui-layout-north { - height: 35px; -} - -.ui-layout-south { - height: 23px; -} - -.ui-layout-east { - height: 250px; -} - -/* -* RESIZER-BARS -*/ -.ui-layout-resizer { /* all 'resizer-bars' */ - background: #DDD; - border: 1px solid #BBB; - border-width: 0; - opacity: 1; /* on-hover, show the resizer-bar normally */ - filter: alpha(opacity=100); - } - - .ui-layout-resizer-open:hover , /* hover-color to 'resize' */ - .ui-layout-resizer-dragging { /* resizer beging 'dragging' */ - background: #EEE; - } - .ui-layout-resizer-dragging { /* CLONED resizer being dragged */ - border-left: 1px solid #BBB; - border-right: 1px solid #BBB; - } - .ui-layout-resizer-drag { /* REAL resizer while resize in progress */ - } - - .ui-layout-resizer-closed:hover { /* hover-color to 'slide open' */ - background: #EEE; - } - .ui-layout-resizer-sliding { /* resizer when pane was 'slid open' */ - opacity: 0.1; /* show only a slight shadow */ - filter: alpha(opacity=10); - } - .ui-layout-resizer-sliding:hover { /* sliding resizer - hover */ - opacity: 1; /* on-hover, show the resizer-bar normally */ - filter: alpha(opacity=100); - } - /* sliding resizer - add 'outside-border' to resizer on-hover */ - .ui-layout-resizer-north-sliding:hover { border-bottom-width: 1px; } - .ui-layout-resizer-south-sliding:hover { border-top-width: 1px; } - .ui-layout-resizer-west-sliding:hover { border-right-width: 1px; } - .ui-layout-resizer-east-sliding:hover { border-left-width: 1px; } - - -/* -* TOGGLER-BUTTONS -*/ -.ui-layout-toggler { - color: #666; - /*border: 1px solid #BBB;*/ /* match pane-border */ - background-color: #999; - } - .ui-layout-toggler:hover { - background-color: #FC6; - } - .ui-layout-toggler-north , - .ui-layout-toggler-south { - border-width: 0 1px; - } - .ui-layout-toggler-west , - .ui-layout-toggler-east { - border-width: 1px 0; - } - /* hide the toggler-button when the pane is 'slid open' */ - .ui-layout-resizer-sliding ui-layout-toggler { - display: none; - } - /* - * style the text we put INSIDE the east/west togglers - */ - .ui-layout-toggler .content { - font: 30px bold Verdana, Arial, Helvetica, sans-serif; - padding-bottom: 0.35ex; /* to 'vertically center' text inside text-span */ - } - -/** - * Actions/Buttons - */ - - #TreeActions button.disabled { - color: #aaa; - } - - .ajaxActions input.disabled, - input.disabled { - color: #666; - } - - input.action.loading, input.action.loading:hover { - padding-left: 22px !important; - background: url(../images/network-save.gif) 3px 2px no-repeat; - } - -input.delete:hover, -button.delete:hover, -.actionparams input.delete:hover { - background: #ce0000; - color: #fff; -} - -input.loading { - padding-left: 16px; - background: #fff url(../images/network-save.gif) no-repeat center left; -} - -input.hidden { - display: none; -} - -/* Overrides - TODO Find a better place to put them */ -form#Form_EditForm fieldset, form#Form_AddForm fieldset { - height: 100%; - border: none; -} -form#Form_EditForm #ScrollPanel fieldset, form#Form_AddForm #ScrollPanel fieldset { - height: auto; -} - -body.stillLoading select { - display: none; -} - -/** 3-PANEL LAYOUT **/ -.ss-cms-top-menu { - overflow: hidden; - font-size: 14px; - height: 33px; - background: #474855 url(../images/mainmenu/top-bg.gif) top left repeat-x; - color: #fff; -} - -/** - * Hidden left-hand panel - */ -#left.hidden form, #left.hidden .title { - display: none; -} - -#left.hidden { - width: 18px; - display: block; -} - -#left div.title, #right div.title { - border-top: 1px solid #77BBEE; - height: 22px !important; - background: #0075C9 url(../images/textures/obar.gif) repeat-x 0 0; -} - -#left div.title div, #right div.title div { - font-size: 14px; - font-weight: bold; - color: #fff; - padding: 2px 0 0 4px; - background-position: 2px 2px; - background-repeat: no-repeat; - padding-left: 20px; - border-top: 1px solid #77BBEE; -} - -#left h2, -#contentPanel h2 { - background-image:url(../images/textures/obar-18.gif); - height: 18px; - line-height: 18px; - color: #fff; - font-size: 12px; - padding-left: 3px; -} - -/** TOP PANEL **/ -.ss-cms-top-menu #MainMenu { - margin: 0 0 0 10px; -} -.ss-cms-top-menu #MainMenu li { - margin: 0 5px; - float: left; - height: 35px; - cursor: pointer; -} - -.ss-cms-top-menu #MainMenu a { - display: block; - height: 33px; - float: left; - padding: 0 10px; - font-size: 14px; - letter-spacing: -0.1px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-weight: normal; - line-height: 32px; - color: #fff; - text-decoration: none; -} - .ss-cms-top-menu #MainMenu a:hover { - text-decoration: none; - background: #6a7580 url(../images/mainmenu/hover.gif) repeat-x left top; - } - -.ss-cms-top-menu #MainMenu .current { - background: #ccdef3 url(../images/mainmenu/current.gif) repeat-x left top; - border-bottom : none; -} - .ss-cms-top-menu #MainMenu .current a:hover { - background: #cddef3 url(../images/mainmenu/currentHover.gif) repeat-x left top; - } - -.ss-cms-top-menu #MainMenu .current a:link, -.ss-cms-top-menu #MainMenu .current a:visited { - color : #2f383f; -} - -.ss-cms-top-menu #Logo { - float: right; -} - .ss-cms-top-menu #Logo a { - margin: 0; - display: block; - font-size: 14px; - text-decoration: none; - height: 26px; - line-height: 22px; - position: absolute; - top: 5px; - right: 8px; - padding-right: 34px; - background: url(../images/mainmenu/logo.gif) right top no-repeat; - color: #fff; - } - html > body .ss-cms-top-menu #Logo { - padding-bottom: 0; - } - -.ss-cms-bottom-bar { - width: 100%; - background: #4d4e5a url(../images/textures/footerBg.gif) repeat-x left top; -} - -.ss-cms-bottom-bar .holder { - text-align: center; - font-size: 10px; - color: #fff; - padding: 0.5em; -} - -.ss-cms-bottom-bar #logInStatus { - float: right; -} - .ss-cms-bottom-bar a { - color: #fff; - background: none; - } - .ss-cms-bottom-bar a:hover { - text-decoration: none; - color: #ccc; - } - .ss-cms-bottom-bar #logInStatus #LogoutLink { - background: url(../images/logout.gif) no-repeat right top; - padding-right: 20px; - } -.ss-cms-bottom-bar #switchView { - float: left; -} - .ss-cms-bottom-bar #switchView a, .ss-cms-bottom-bar #switchView span { - background: none; - padding-left: 8px; - padding-right: 8px; - display: inline-block; - border-left: 1px solid #AAA; - } - .ss-cms-bottom-bar #switchView span { - border-left: none; - } -.ss-cms-bottom-bar .bottomTabs a { - color: #fff; -} - -.ss-cms-bottom-bar .bottomTabs div.blank { - display: block; - float: left; - cursor: pointer; - background-color: transparent; - padding-right: 4px; - font-weight: bold; - line-height: 13px; - font-size: 11px; - padding-left: 2px; - color: #fff; -} -.ss-cms-bottom-bar .bottomTabs .current { - color: #ccc; -} - .ss-cms-bottom-bar .bottomTabs a:hover { - color: #ccc; - text-decoration: none; - } - - -/** LEFT PANEL **/ -#sitetree_and_tools { - overflow: auto; -} - - #sitetree_holder #TreeTools { - background: #EEE; - } - #SearchControls { - margin-top:2px; - } - #SearchControls label { - display: none; - } - -#left #TreeActions, -#left .checkboxAboveTree { - background: #EEE; - padding: 5px; - float: left; - width: 95%; -} -#left .checkboxAboveTree { - border-bottom: 1px solid #CCC; -} - -#TreeTools label { - display: block; - padding-top: 2px; - _padding-top: 4px; -} - -#TreeTools .ui-tabs .ui-tabs-nav li a { - padding-left: .5em; - padding-right: .5em; -} - -#TreeTools #batchactionsforms { - padding: 0 5px 7px 5px; -} - #TreeTools select { - margin-left: 2px; - } - #TreeTools div p, #ShowChanged { - margin: 0 0 5px 0; - float: left; - width: 100%; - } - #TreeTools div.middleColumn { - margin: 0; - } - #TreeTools #action_publish_selected, - #TreeTools #action_publish_selected { - padding: 2px 1px; - float: left; - } - -#checkboxActionIndicator { - float: right; - width: auto; - margin-top: 7px; - display: none; -} - -div.spacer, -li.spacer { - float: none; - clear: both; - background-color: transparent; - border-style: none; - margin: -1px 0 0 0; - height: 1px; - font-size: 1px; - width: auto; -} -/** RIGHT PANEL **/ -.mceToolbarExternal { - background-color: #eee; - display: block; -} - .mceToolbarExternal a { - text-decoration: none; - border: 0 !important; - } - .mceToolbarExternal a img { - border: none; - } - -#right form#Form_EditorToolbarLinkForm, -#right form#Form_EditorToolbarImageForm, -#right form#Form_EditorToolbarFlashForm { - background: #eee; - border-bottom: 1px solid #ccc; - display: none; - margin: 1px 0 0 0; - padding: 5px; - /* - * HACK IE (all versions): container needs to be higher stacking order - * than any DOM-elemnt under it. - * @see http://www.aplus.co.yu/lab/z-pos/ - */ - /*overflow: hidden;*/ - z-index: 1001; -} - - -#right form#Form_EditorToolbarLinkForm ul.optionset { - height: 1em; -} - -#right form#Form_EditorToolbarLinkForm ul.optionset li { - float: left; -} - -.thumbnailstrip { - overflow: auto; - white-space: nowrap; - float: left; - border: 1px solid #aaa; -} - -.thumbnailstrip li { - float: left; - margin-right: 4px; -} - -/* Misc Styling */ -iframe { - border: none; -} -/* Content Panel Design - * This is our new right hand pane for inserting images and links etc - */ -#contentPanel { - font-size: 12px; - -moz-border-radius-topleft: 4px; - -moz-border-radius-bottomleft: 4px; - border: 1px solid #AAA; -} -#contentPanel .content { - overflow: auto; -} - -#contentPanel div, -#contentPanel p#TargetBlank { - margin-left: 0; -} - -#contentPanel .thumbnailstrip { - border: none; - width: 190px; - height: 120px; - overflow-y: auto; - margin-right: 0 !important; -} - #contentPanel .ui-dialog { - width: auto; - } - #contentPanel .thumbnailstrip h2 { - font-size: 1.1em; - margin: 0; - background: none; - color: #555; - height: auto; - } -#contentPanel select { - width: 186px; - padding: 1px 0; - font-size: 12px; -} -#contentPanel option { - font-size: 12px; -} -#contentPanel .middleColumn { - background:#E9E9E9 none repeat scroll 0%; - display:block; - padding:3px; -} -#contentPanel input.text { - border:1px solid #A7A7A7; - padding:3px; - width: 179px; -} -#contentPanel #Dimensions div.middleColumn { - background: none; -} - -#contentPanel form { - display: none; -} - -/* Image height and width inputs. We need to position them in the right places */ -#contentPanel input#Form_EditorToolbarImageForm_Width, -#contentPanel input#Form_EditorToolbarFlashForm_Width { - width: 30px; -} -#contentPanel input#Form_EditorToolbarImageForm_Height, -#contentPanel input#Form_EditorToolbarFlashForm_Height { - width: 30px; -} -#Form_EditorToolbarImageForm #FolderImages, -#Form_EditorToolbarFlashForm #Flash { - margin: 5px; -} -#Form_EditorToolbarImageForm fieldset label.left, -#Form_EditorToolbarFlashForm fieldset label.left, -#Form_EditorToolbarLinkForm fieldset label.left { - float:left; - margin-left:0px; - color: #666; - line-height: 1.2; - font-size: 11px; - width: 190px; -} -#Form_EditorToolbarImageForm fieldset div.middleColumn, -#Form_EditorToolbarFlashForm fieldset div.middleColumn, -#Form_EditorToolbarLinkForm fieldset div.middleColumn { - float: left; -} -#Form_EditorToolbarLinkForm fieldset li div.middleColumn, -#Form_EditorToolbarFlashForm fieldset li div.middleColumn, -#Form_EditorToolbarImageForm fieldset li div.middleColumn { - float: none; -} - -#Form_EditorToolbarImageForm fieldset div.fieldgroupField, -#Form_EditorToolbarFlashForm fieldset div.fieldgroupField, -#Form_EditorToolbarLinkForm fieldset div.fieldgroupField { - display: inline; -} - -#Form_EditorToolbarLinkForm .buttonRefresh { - display:block; - border:1px solid #F0F0EE; - width:20px; height:20px; - float: left; -} -#Form_EditorToolbarLinkForm a.buttonRefreshHover { - border: 1px solid #0A246A; - background-color:#B2BBD0; -} - -#Form_EditorToolbarLinkForm a. -.defaultSkin .mceButtonDisabled .mceIcon {opacity:0.3; -ms-filter:'alpha(opacity=30)'; filter:alpha(opacity=30)} - -#Form_EditorToolbarLinkForm a.buttonRefresh span { - display: block; - width: 20px; - height: 20px; - background: transparent url(../../cms/images/arrow_refresh.gif) no-repeat; -} - -#Form_EditorToolbarLinkForm select.hasRefreshButton { - width: 160px; -} - -#Form_EditorToolbarImageForm_Width, -#Form_EditorToolbarImageForm_Height, -#Form_EditorToolbarFlashForm_Width, -#Form_EditorToolbarFlashForm_Height { - outline: 3px solid #E9E9E9; -} - -#Form_EditorToolbarImageForm #FolderImages img, -#Form_EditorToolbarFlashForm #Flash img { - border: 2px solid #e4e3e3; -} -#contentPanel div.Actions { - margin: 5px; - text-align: right; -} -#contentPanel #FolderImages a.selectedImage img, -#contentPanel #Flash a.selectedFlash img { - border: 2px solid #222; -} - - /* going to use this */ -#BlankImage { - text-indent: -9000px; - -} - -/** - * Messages (see sapphire/css/Form.css) - */ -.message { - margin: 1em 0; - padding: 0.5em; - font-weight: bold; - border: 1px black solid; - background-color: #B9FFB9; - border-color: #00FF00; -} - .message.notice { - background-color: #FCFFDF; - border-color: #FF9300; - } - - .message.warning { - background-color: #FFD2A6; - border-color: #FF9300; - } - .message.bad { - background-color: #FF8080; - border-color: #FF0000; - } diff --git a/css/silverstripe.tabs.css b/css/silverstripe.tabs.css deleted file mode 100644 index 3b99659b..00000000 --- a/css/silverstripe.tabs.css +++ /dev/null @@ -1,31 +0,0 @@ -/** - * SilverStripe specific styles that map to elements from the jQuery UI theme. - * - * This is not a fully functional tab module, but the file naming convention reflects - * the jQuery theme format - * - * @todo move generic tab styles into this file - * @todo add to silverstripe specific branch of /sapphire/thirdparty/jquery-ui-themes - */ - -/* Caution! Ensure accessibility in print and other media types... */ -@media projection, screen { /* Use class for showing/hiding tab content, so that visibility can be better controlled in different media types... */ - .ui-tabs-hide { - display: none; - } -} - -/* Hide useless elements in print layouts... */ -@media print { - .ui-tabs-nav { - display: none; - } -} - -/** - * stuff for search forms -*/ -.loading { - cursor:hourglass; -} - \ No newline at end of file diff --git a/css/typography.css b/css/typography.css deleted file mode 100644 index f4937ade..00000000 --- a/css/typography.css +++ /dev/null @@ -1,34 +0,0 @@ -* { - font-family: Arial, Helvetica, sans-serif; - font-size: 10px; -} - -p, ul, td, th { - font-size: 12px; -} - -h1, h2 { - line-height: 2; - color: #333; -} - h1 { - font-size: 20px; - } - h2 { - font-size: 18px; - } - -a { - font-size: inherit; - color: #0074C6; - text-decoration: underline; -} - -a:hover { - text-decoration: none; - background: #ccc; -} - -a img { - border-style: none; -} diff --git a/css/unjquery.css b/css/unjquery.css deleted file mode 100644 index 846ac645..00000000 --- a/css/unjquery.css +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Clean up border overkill - */ - -.ui-widget-header { - background: none; - border: none; - border-bottom: 1px solid #AAAAAA; -} - -#Root.ui-widget-content { - border: 1px solid #AAAAAA; -} - -.ui-widget-content { - border: none; -} - -.ui-accordion .ui-accordion-content-active { - border: 1px solid #AAA; - border-top: none; -} - -.CMSMain .ui-tabs .ui-corner-all { - -moz-border-radius: 0; - -webkit-border-radius: 0; -} - - -/* tabs 'reset' */ -.CMSMain .ui-tabs, -.CMSMain .ui-tabs .ui-tabs-panel { - padding: 0; -} - -#Root { - -moz-border-radius-bottomleft: 0; - -moz-border-radius-bottomright: 0; - -webkit-border-bottom-left-radius: 0; - -webkit-border-bottom-right-radius: 0; -} - -.CMSMain .ui-tabs .ui-tabs-nav { - padding: 0.5em 0.5em 0 0.5em; -} - -.CMSMain .ui-tabs .tab { - padding: 1em; -} - -.CMSMain .ui-tabs .tab .ss-tabset { - margin: -0.5em -1em -1em -1em; -} - - -.CMSMain .ui-tabs .ui-tabs-nav li.ui-tabs-selected { - border-top:2px solid orange; - padding-bottom:0; -} - - -.ui-layout-pane-center, -.ui-layout-west { - background: none; -} - -#treepanes .ui-widget input { - font-size: 1.1em; -} - -#treepanes .ui-tabs-panel { - padding: 0.5em; - border-bottom: #AAA 1px solid; -} - -#treepanes .ui-tabs-panel.ui-corner-bottom { - -moz-border-radius: 0; - -webkit-border-radius: 0; -} - -#treepanes .ui-accordion-header.ui-corner-top, -#treepanes .ui-accordion-header.ui-corner-bottom { - -moz-border-radius-topleft: 0; - -webkit-border-top-left-radius: 0; -} - -#treepanes .ui-corner-bottom { - -moz-border-radius-bottomleft: 0; - -moz-border-radius-bottomright: 4px; - -webkit-border-bottom-left-radius: 0; - -webkit-border-bottom-right-radius: 4px; -} - -#treepanes .ui-accordion-header a { - font-size: 120%; -} -#treepanes.ui-accordion .ui-accordion-header .ui-icon { - right: 0.5em !important; - left: none !important; -} - -#treepanes #TreeFooter { - border-bottom: 1px solid #ccc; - -moz-border-radius-bottomright: 4px; - -webkit-border-bottom-right-radius: 4px; -} - -#treepanes .tree-panel { - padding: 0.5em; - background: #EEE; -} - -.ui-tabs a { - font-size: 120%; -} - -.right form { - margin: 1px 0 0 0; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; -} - -h2#Form_EditForm_WelcomeHeader, -p#WelcomeMessage { - margin: 1em 0 0 25px; -} - -.ui-layout-resizer { - background: #ccdef3; -} -.ui-layout-resizer-open:hover, -.ui-layout-resizer-dragging { - background: #ccdef3 none repeat scroll 0 0; -} diff --git a/images/alert-bad.gif b/images/alert-bad.gif deleted file mode 100755 index 98529dd5..00000000 Binary files a/images/alert-bad.gif and /dev/null differ diff --git a/images/alert-good.gif b/images/alert-good.gif deleted file mode 100755 index 856fc40c..00000000 Binary files a/images/alert-good.gif and /dev/null differ diff --git a/images/logo.gif b/images/logo.gif deleted file mode 100644 index 77642f24..00000000 Binary files a/images/logo.gif and /dev/null differ diff --git a/images/logout.gif b/images/logout.gif deleted file mode 100644 index 6519af0a..00000000 Binary files a/images/logout.gif and /dev/null differ diff --git a/images/mainmenu/content.png b/images/mainmenu/content.png deleted file mode 100755 index 7fae7489..00000000 Binary files a/images/mainmenu/content.png and /dev/null differ diff --git a/images/mainmenu/current.gif b/images/mainmenu/current.gif deleted file mode 100644 index 83985641..00000000 Binary files a/images/mainmenu/current.gif and /dev/null differ diff --git a/images/mainmenu/currentHover.gif b/images/mainmenu/currentHover.gif deleted file mode 100644 index 9f3371b2..00000000 Binary files a/images/mainmenu/currentHover.gif and /dev/null differ diff --git a/images/mainmenu/help.gif b/images/mainmenu/help.gif deleted file mode 100755 index 4f87eac3..00000000 Binary files a/images/mainmenu/help.gif and /dev/null differ diff --git a/images/mainmenu/help.png b/images/mainmenu/help.png deleted file mode 100755 index 7324316a..00000000 Binary files a/images/mainmenu/help.png and /dev/null differ diff --git a/images/mainmenu/hover.gif b/images/mainmenu/hover.gif deleted file mode 100644 index 0e769f82..00000000 Binary files a/images/mainmenu/hover.gif and /dev/null differ diff --git a/images/mainmenu/logo.gif b/images/mainmenu/logo.gif deleted file mode 100755 index c0baae74..00000000 Binary files a/images/mainmenu/logo.gif and /dev/null differ diff --git a/images/mainmenu/logo.png b/images/mainmenu/logo.png deleted file mode 100755 index c981e6ec..00000000 Binary files a/images/mainmenu/logo.png and /dev/null differ diff --git a/images/mainmenu/top-bg.gif b/images/mainmenu/top-bg.gif deleted file mode 100755 index 853725ab..00000000 Binary files a/images/mainmenu/top-bg.gif and /dev/null differ diff --git a/images/textures/ToolBar.png b/images/textures/ToolBar.png deleted file mode 100755 index dd4189b1..00000000 Binary files a/images/textures/ToolBar.png and /dev/null differ diff --git a/images/textures/bottom.png b/images/textures/bottom.png deleted file mode 100755 index ca68f228..00000000 Binary files a/images/textures/bottom.png and /dev/null differ diff --git a/images/textures/footerBg.gif b/images/textures/footerBg.gif deleted file mode 100644 index d1099e8a..00000000 Binary files a/images/textures/footerBg.gif and /dev/null differ diff --git a/images/textures/mce_editor.gif b/images/textures/mce_editor.gif deleted file mode 100644 index 313bd8b7..00000000 Binary files a/images/textures/mce_editor.gif and /dev/null differ diff --git a/images/textures/obar-18.gif b/images/textures/obar-18.gif deleted file mode 100755 index 58344443..00000000 Binary files a/images/textures/obar-18.gif and /dev/null differ diff --git a/images/textures/obar-light.png b/images/textures/obar-light.png deleted file mode 100755 index bee14105..00000000 Binary files a/images/textures/obar-light.png and /dev/null differ diff --git a/images/textures/obar.gif b/images/textures/obar.gif deleted file mode 100755 index 679b9aa5..00000000 Binary files a/images/textures/obar.gif and /dev/null differ diff --git a/images/textures/seperator.png b/images/textures/seperator.png deleted file mode 100755 index f3fd4189..00000000 Binary files a/images/textures/seperator.png and /dev/null differ diff --git a/javascript/AssetTableField.js b/javascript/AssetTableField.js deleted file mode 100644 index 0c0a7808..00000000 --- a/javascript/AssetTableField.js +++ /dev/null @@ -1,169 +0,0 @@ -/** - * File: AssetTableField.js - */ -(function($) { - $.entwine('ss', function($){ - - /** - * Class: .AssetTableField - */ - $('.AssetTableField').entwine({ - // Constructor: onmatch - onmatch: function() { - var self = this; - - // search button - this.find('input#FileFilterButton').click(function(e) { - var btn = $(this); - $(this).addClass('loading'); - self.refresh(function() {btn.removeClass('loading');}); - return false; - }); - - // clear button - this.find('input#FileFilterClearButton').click(function(e) { - self.find('input#FileSearch').val(''); - self.find('input#FileFilterButton').click(); - return false; - }); - - // search field - this.find('input#FileSearch').keypress(function(e) { - if(e.keyCode == $.ui.keyCode.ENTER) { - self.find('input#FileFilterButton').click(); - } - }); - - this._super(); - }, - - /** - * Function: refresh - * - * Parameters: - * (Function) callback - */ - refresh: function(callback) { - var self = this; - this.load( - this.attr('href'), - this.find(':input').serialize(), - function(response, status, xmlhttp) { - Behaviour.apply(self[0], true); - if(callback) callback.apply(arguments); - } - ); - } - }); - - /** - * Class: .AssetTableField :checkbox - * - * Checkboxes used to batch delete files - */ - $('.AssetTableField :checkbox').entwine({ - // Function: onchange - onchange: function() { - var container = this.parents('.AssetTableField'); - var input = container.find('input#deletemarked'); - if(container.find(':input[name=Files\[\]]:checked').length) { - input.removeAttr('disabled'); - } else { - input.attr('disabled', 'disabled'); - } - } - }) - - /** - * Class: .AssetTableField input#deletemarked - * - * Batch delete files marked by checkboxes in the table. - * Refreshes the form field afterwards via ajax. - */ - $('.AssetTableField input#deletemarked').entwine({ - // Constructor: onmatch - onmatch: function() { - this.attr('disabled', 'disabled'); - this._super(); - }, - - /** - * Function: onclick - * - * Parameters: - * (Event) e - */ - onclick: function(e) { - if(!confirm(ss.i18n._t('AssetTableField.REALLYDELETE'))) return false; - - var container = this.parents('.AssetTableField'); - var self = this; - this.addClass('loading'); - $.post( - container.attr('href') + '/deletemarked', - this.parents('form').serialize(), - function(data, status) { - self.removeClass('loading'); - container.refresh(); - } - ); - return false; - } - }); - }); -}(jQuery)); - -// TODO Implementation in Behaviour instead of entwine is necessary to overload TableListField -var AssetTableField = Class.create(); -AssetTableField.applyTo('#Form_EditForm_Files'); -AssetTableField.prototype = { - initialize: function() { - var rules = {}; - rules['#'+this.id+' table.data a.deletelink'] = {onclick: this.deleteRecord.bind(this)}; - Behaviour.register('ComplexTableField_'+this.id,rules); - }, - - deleteRecord: function(e) { - var img = Event.element(e); - var link = Event.findElement(e,"a"); - var row = Event.findElement(e,"tr"); - var self = this; - - var linkCount = row.getElementsByClassName('linkCount')[0]; - if(linkCount) linkCount = linkCount.innerHTML; - - var confirmMessage = ss.i18n._t('TABLEFIELD.DELETECONFIRMMESSAGE', 'Are you sure you want to delete this record?'); - if(linkCount && linkCount > 0) confirmMessage += '\nThere are ' + linkCount + ' page(s) that use this file, please review the list of pages on the Links tab of the file before continuing.'; - - // TODO ajaxErrorHandler and loading-image are dependent on cms, but formfield is in sapphire - var confirmed = confirm(confirmMessage); - if(confirmed) - { - img.setAttribute("src",'cms/images/network-save.gif'); // TODO doesn't work - jQuery.ajax({ - 'url': link.getAttribute("href"), - 'method': 'post', - 'data': {'forceajax': 1, 'SecurityID': $('SecurityID') ? $('SecurityID').value : null}, - 'success': function(){ - Effect.Fade( - row, - { - afterFinish: function(obj) { - // remove row from DOM - obj.element.parentNode.removeChild(obj.element); - // recalculate summary if needed (assumes that TableListField.js is present) - // TODO Proper inheritance - if(self._summarise) self._summarise(); - // custom callback - if(self.callback_deleteRecord) self.callback_deleteRecord(e); - } - } - ); - }, - 'error': this.ajaxErrorHandler - }); - } - - Event.stop(e); - } -}; \ No newline at end of file diff --git a/javascript/LeftAndMain.AddForm.js b/javascript/LeftAndMain.AddForm.js deleted file mode 100644 index fb8024a9..00000000 --- a/javascript/LeftAndMain.AddForm.js +++ /dev/null @@ -1,163 +0,0 @@ -/** - * File: LeftAndMain.AddForm.js - */ -(function($) { - $.entwine('ss', function($){ - /** - * Class: #Form_AddForm - * - * Simple form with a page type dropdown - * which creates a new page through #Form_EditForm and adds a new tree node. - * - * Requires: - * ss.i18n - * #Form_EditForm - */ - $('#Form_AddForm').entwine({ - /** - * Variable: Tree - * (DOMElement) - */ - Tree: null, - - /** - * Variable: OrigOptions - * (Array) Map of ')); - } - } else { - // No tree node selected, reset to original elements - $.each(origOptions, function(i, optProps) { - if(optProps) dropdown.append($('')); - }); - } - - // TODO Re-select the currently selected element - - // Disable dropdown if no elements are selectable - if(allowed) dropdown.removeAttr('disabled'); - else dropdown.attr('disabled', 'disabled'); - - // Set default child (optional) - if(selectedNode.hints && selectedNode.hints.defaultChild) { - dropdown.val(selectedNode.hints.defaultChild); - } - - // Set parent node (fallback to root) - this.find(':input[name=ParentID]').val(selectedNode ? selectedNode.data('id') : 0); - } - }); - }); -}(jQuery)); \ No newline at end of file diff --git a/javascript/LeftAndMain.BatchActions.js b/javascript/LeftAndMain.BatchActions.js deleted file mode 100644 index 124c8457..00000000 --- a/javascript/LeftAndMain.BatchActions.js +++ /dev/null @@ -1,355 +0,0 @@ -/** - * File: LeftAndMain.BatchActions.js - */ -(function($) { - $.entwine('ss', function($){ - - /** - * Class: #Form_BatchActionsForm - * - * Batch actions which take a bunch of selected pages, - * usually from the CMS tree implementation, and perform serverside - * callbacks on the whole set. We make the tree selectable when the jQuery.UI tab - * enclosing this form is opened. - * - * Events: - * register - Called before an action is added. - * unregister - Called before an action is removed. - */ - $('#Form_BatchActionsForm').entwine({ - - /** - * Variable: Tree - * (DOMElement) - */ - Tree: null, - - /** - * Variable: Actions - * (Array) Stores all actions that can be performed on the collected IDs as - * function closures. This might trigger filtering of the selected IDs, - * a confirmation message, etc. - */ - Actions: [], - - /** - * Constructor: onmatch - */ - onmatch: function() { - var self = this, tree = $('#sitetree_ul'); - - this.setTree(tree); - - tree.bind('check_node.jstree', function(e, data) { - self.serializeFromTree(); - }); - - // if tab which contains this form is shown, make the tree selectable - $('#TreeActions').bind('tabsselect', function(e, ui) { - // 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'); - self.serializeFromTree(); - } - }); - - this._super(); - }, - - /** - * Function: register - * - * Parameters: - * - * (String) type - ... - * (Function) callback - ... - */ - register: function(type, callback) { - this.trigger('register', {type: type, callback: callback}); - var actions = this.getActions(); - actions[type] = callback; - this.setActions(actions); - }, - - /** - * Function: unregister - * - * Remove an existing action. - * - * Parameters: - * - * {String} type - */ - unregister: function(type) { - this.trigger('unregister', {type: type}); - - var actions = this.getActions(); - if(actions[type]) delete actions[type]; - this.setActions(actions); - }, - - /** - * Function: _isActive - * - * Determines if we should allow and track tree selections. - * - * Todo: - * Too much coupling with tabset - * - * Returns: - * (boolean) - */ - _isActive: function() { - return $('#TreeActions-batchactions').is(':visible'); - }, - - /** - * Function: refreshSelected - * - * Ajax callbacks determine which pages is selectable in a certain batch action. - * - * Parameters: - * {Object} rootNode - */ - refreshSelected : function(rootNode) { - var self = this, st = this.getTree(), ids = this.getIDs(), allIds = []; - // Default to refreshing the entire tree - if(rootNode == null) rootNode = st; - - for(var idx in ids) { - $($(st).getNodeByID(idx)).addClass('selected').attr('selected', 'selected'); - } - - $(rootNode).find('li').each(function() { - allIds.push($(this).data('id')); - - // Disable the nodes while the ajax request is being processed - $(this).addClass('treeloading').setEnabled(false); - }); - - // Post to the server to ask which pages can have this batch action applied - var applicablePagesURL = this.find(':input[name=Action]').val() + '/applicablepages/?csvIDs=' + allIds.join(','); - jQuery.getJSON(applicablePagesURL, function(applicableIDs) { - // Set a CSS class on each tree node indicating which can be batch-actioned and which can't - jQuery(rootNode).find('li').each(function() { - $(this).removeClass('treeloading'); - - var id = $(this).data('id'); - if(id == 0 || $.inArray(id, applicableIDs) >= 0) { - $(this).setEnabled(true); - } else { - // De-select the node if it's non-applicable - $(this).removeClass('selected').setEnabled(false); - } - }); - - self.serializeFromTree(); - }); - }, - - /** - * Function: serializeFromTree - * - * Returns: - * (boolean) - */ - serializeFromTree: function() { - var tree = this.getTree(), ids = tree.getSelectedIDs(); - - // if no IDs are selected, stop here. This is an implict way for the - // callback to cancel the actions - if(!ids || !ids.length) return false; - - // write IDs to the hidden field - this.setIDs(ids); - - return true; - }, - - /** - * Function: setIDS - * - * Parameters: - * {Array} ids - */ - setIDs: function(ids) { - if(ids) this.find(':input[name=csvIDs]').val(ids.join(',')); - }, - - /** - * Function: getIDS - * - * Returns: - * {Array} - */ - getIDs: function() { - return this.find(':input[name=csvIDs]').val().split(','); - }, - - /** - * Function: onsubmit - * - * Parameters: - * (Event) e - */ - onsubmit: function(e) { - var ids = this.getIDs(); - var tree = this.getTree(); - - // if no nodes are selected, return with an error - if(!ids || !ids.length) { - alert(ss.i18n._t('CMSMAIN.SELECTONEPAGE')); - return false; - } - - // apply callback, which might modify the IDs - var type = this.find(':input[name=Action]').val(); - if(this.getActions()[type]) ids = this.getActions()[type].apply(this, [ids]); - - // write (possibly modified) IDs back into to the hidden field - this.setIDs(ids); - - // Reset failure states - tree.find('li').removeClass('failed'); - - var button = this.find(':submit:first'); - button.addClass('loading'); - - jQuery.ajax({ - // don't use original form url - url: type, - type: 'POST', - data: this.serializeArray(), - complete: function(xmlhttp, status) { - button.removeClass('loading'); - - // status message - var msg = (xmlhttp.getResponseHeader('X-Status')) ? xmlhttp.getResponseHeader('X-Status') : xmlhttp.statusText; - statusMessage(msg, (status == 'success') ? 'good' : 'bad'); - }, - success: function(data, status) { - var id; - - // TODO This should use a more common serialization in a new tree library - if(data.modified) { - for(id in data.modified) { - tree.jstree('set_title', tree.getNodeByID(id), data.modified[id]['TreeTitle']); - } - } - if(data.deleted) { - for(id in data.deleted) { - var node = tree.getNodeByID(id); - // TODO Remove node - // if(node && node.parentTreeNode) node.parentTreeNode.removeTreeNode(node); - } - } - if(data.error) { - for(id in data.error) { - var node = tree.getNodeByID(id); - $(node).addClass('failed'); - } - } - - // Deselect all nodes - tree.find('li').removeClass('selected'); - - // reset selection state - // TODO Should unselect all selected nodes as well - tree.removeClass('multiple'); - - // TODO Fix up to work properly with jstree - unclear if state setting is still required in new design - // // Check if current page still exists, and refresh it. - // // Otherwise remove the current form - // var selectedNode = tree.jstree('get_selected'); - // if(selectedNode) { - // var selectedNodeId = selectedNode.getID(); - // if(data.modified[selectedNodeId]) { - // // only if the current page was modified - // tree.jstree('select_node', selectedNode); - // } else if(data.deleted[selectedNodeId]) { - // jQuery('#Form_EditForm').entwine('ss').removeForm(); - // } - // } else { - // jQuery('#Form_EditForm').entwine('ss').removeForm(); - // } - - // close panel - // TODO Coupling with tabs - // jQuery('#TreeActions').tabs('select', -1); - }, - dataType: 'json' - }); - - return false; - } - - }); - }); - - /** - * Class: #Form_BatchActionsForm :select[name=Action] - */ - $('#Form_BatchActionsForm select[name=Action]').entwine({ - - /** - * Function: onchange - * - * Parameters: - * (Event) e - */ - onchange: function(e) { - $(e.target.form).entwine('ss').refreshSelected(); - } - }); - - $(document).ready(function() { - /** - * Publish selected pages action - */ - $('#Form_BatchActionsForm').entwine('ss').register('admin/batchactions/publish', function(ids) { - var confirmed = confirm( - "You have " + ids.length + " pages selected.\n\n" - + "Do your really want to publish?" - ); - return (confirmed) ? ids : false; - }); - - /** - * Unpublish selected pages action - */ - $('#Form_BatchActionsForm').entwine('ss').register('admin/batchactions/unpublish', function(ids) { - var confirmed = confirm( - "You have " + ids.length + " pages selected.\n\n" - + "Do your really want to unpublish?" - ); - return (confirmed) ? ids : false; - }); - - /** - * Delete selected pages action - */ - $('#Form_BatchActionsForm').entwine('ss').register('admin/batchactions/delete', function(ids) { - var confirmed = confirm( - "You have " + ids.length + " pages selected.\n\n" - + "Do your really want to delete?" - ); - return (confirmed) ? ids : false; - }); - - /** - * Delete selected pages from live action - */ - $('#Form_BatchActionsForm').entwine('ss').register('admin/batchactions/deletefromlive', function(ids) { - var confirmed = confirm( - "You have " + ids.length + " pages selected.\n\n" - + "Do your really want to delete these pages from live?" - ); - return (confirmed) ? ids : false; - }); - }); - -})(jQuery); \ No newline at end of file diff --git a/javascript/LeftAndMain.EditForm.js b/javascript/LeftAndMain.EditForm.js deleted file mode 100644 index 7b510838..00000000 --- a/javascript/LeftAndMain.EditForm.js +++ /dev/null @@ -1,382 +0,0 @@ -/** - * File: LeftAndMain.EditForm.js - */ -(function($) { - $.entwine('ss', function($){ - - /** - * Class: #Form_EditForm - * - * Base edit form, provides ajaxified saving - * and reloading itself through the ajax return values. - * Takes care of resizing tabsets within the layout container. - * @name ss.Form_EditForm - * @require jquery.changetracker - * - * Events: - * ajaxsubmit - Form is about to be submitted through ajax - * validate - Contains validation result - * removeform - A form is about to be removed from the DOM - * load - Form is about to be loaded through ajax - */ - $('#Form_EditForm').entwine(/** @lends ss.Form_EditForm */{ - /** - * Variable: PlaceholderHtml - * (String_ HTML text to show when no form content is chosen. - * Will show inside the
    tag. - */ - PlaceholderHtml: '', - - /** - * Variable: ChangeTrackerOptions - * (Object) - */ - ChangeTrackerOptions: {}, - - /** - * Constructor: onmatch - */ - onmatch: function() { - var self = this; - - this._setupChangeTracker(); - - // Can't bind this through jQuery - window.onbeforeunload = function(e) {return self._checkChangeTracker(false);}; - - this._super(); - }, - - /** - * Function: _setupChangeTracker - */ - _setupChangeTracker: function() { - // Don't bind any events here, as we dont replace the - // full tag by any ajax updates they won't automatically reapply - this.changetracker(this.getChangeTrackerOptions()); - }, - - /** - * Function: _checkChangeTracker - * - * Checks the jquery.changetracker plugin status for this form. - * Usually bound to window.onbeforeunload. - * - * Parameters: - * {boolean} isUnloadEvent - .. - * - * Returns: - * (String) Either a string with a confirmation message, or the result of a confirm() dialog, - * based on the isUnloadEvent parameter. - */ - _checkChangeTracker: function(isUnloadEvent) { - var self = this; - - // @todo TinyMCE coupling - if(typeof tinyMCE != 'undefined') tinyMCE.triggerSave(); - - // check for form changes - if(self.is('.changed')) { - // returned string will trigger a confirm() dialog, - // but only if the method is triggered by an event - if(isUnloadEvent) { - return confirm(ss.i18n._t('LeftAndMain.CONFIRMUNSAVED')); - } else { - return ss.i18n._t('LeftAndMain.CONFIRMUNSAVEDSHORT'); - } - } - }, - - /** - * Function: onsubmit - * - * Suppress submission unless it is handled through ajaxSubmit(). - */ - onsubmit: function(e) { - this.ajaxSubmit(); - - return false; - }, - - /** - * Function: ajaxSubmit - * - * Parameters: - * {DOMElement} button - The pressed button (optional) - * {Function} callback - Called in complete() handler of jQuery.ajax() - * {Object} ajaxOptions - Object literal to merge into $.ajax() call - * {boolean} loadResponse - Render response through _loadResponse() (Default: true) - * - * Returns: - * (boolean) - */ - ajaxSubmit: function(button, callback, ajaxOptions, loadResponse) { - var self = this; - - // look for save button - if(!button) button = this.find('.Actions :submit[name=action_save]'); - // default to first button if none given - simulates browser behaviour - if(!button) button = this.find('.Actions :submit:first'); - - this.trigger('ajaxsubmit', {button: button}); - - // set button to "submitting" state - $(button).addClass('loading'); - - // @todo TinyMCE coupling - if(typeof tinyMCE != 'undefined') tinyMCE.triggerSave(); - - // validate if required - if(!this.validate()) { - // TODO Automatically switch to the tab/position of the first error - statusMessage("Validation failed.", "bad"); - - $(button).removeClass('loading'); - - return false; - } - - // save tab selections in order to reconstruct them later - var selectedTabs = []; - this.find('.ss-tabset').each(function(i, el) { - if($(el).attr('id')) selectedTabs.push({id:$(el).attr('id'), selected:$(el).tabs('option', 'selected')}); - }); - - // get all data from the form - var formData = this.serializeArray(); - // add button action - formData.push({name: $(button).attr('name'), value:'1'}); - jQuery.ajax(jQuery.extend({ - url: this.attr('action'), - data: formData, - type: 'POST', - complete: function(xmlhttp, status) { - $(button).removeClass('loading'); - - // TODO This should be using the plugin API - self.removeClass('changed'); - - if(callback) callback(xmlhttp, status); - - // pass along original form data to enable old/new comparisons - if(loadResponse !== false) { - self._loadResponse(xmlhttp.responseText, status, xmlhttp, formData); - } - - // re-select previously saved tabs - $.each(selectedTabs, function(i, selectedTab) { - self.find('#' + selectedTab.id).tabs('select', selectedTab.selected); - }); - }, - dataType: 'html' - }, ajaxOptions)); - - return false; - }, - - /** - * Function: validate - * - * Hook in (optional) validation routines. - * Currently clientside validation is not supported out of the box in the CMS. - * - * Todo: - * Placeholder implementation - * - * Returns: - * {boolean} - */ - validate: function() { - var isValid = true; - this.trigger('validate', {isValid: isValid}); - - return isValid; - }, - - /** - * Function: loadForm - * - * Parameters: - * (String) url - .. - * (Function) callback - (Optional) Called after the form content as been loaded - * (Object) ajaxOptions - Object literal merged into the jQuery.ajax() call (Optional) - * - * Returns: - * (XMLHTTPRequest) - */ - loadForm: function(url, callback, ajaxOptions) { - var self = this; - - // Alert when unsaved changes are present - if(this._checkChangeTracker(true) == false) return false; - - // hide existing form - shown again through _loadResponse() - this.addClass('loading'); - - this.trigger('load', {url: url}); - - this.cleanup(); - - return jQuery.ajax(jQuery.extend({ - url: url, - complete: function(xmlhttp, status) { - // TODO This should be using the plugin API - self.removeClass('changed'); - - self._loadResponse(xmlhttp.responseText, status, xmlhttp); - - self.removeClass('loading'); - - if(callback) callback.apply(self, arguments); - }, - dataType: 'html' - }, ajaxOptions)); - }, - - /** - * Function: removeForm - * - * Remove everying inside the tag - * with a custom HTML fragment. Useful e.g. for deleting a page in the CMS. - * Checks for unsaved changes before removing the form - * - * Parameters: - * {String} placeholderHtml - Short note why the form has been removed, displayed in

    tags. - * Falls back to the default RemoveText() option (Optional) - */ - removeForm: function(placeholderHtml) { - if(!placeholderHtml) placeholderHtml = this.getPlaceholderHtml(); - // Alert when unsaved changes are present - if(this._checkChangeTracker(true) == false) return; - this.trigger('removeform'); - this.html(placeholderHtml); - // TODO This should be using the plugin API - this.removeClass('changed'); - }, - - /** - * Function: cleanup - * - * Remove all the currently active TinyMCE editors. - * Note: Everything that calls this externally has an inappropriate coupling to TinyMCE. - */ - cleanup: function() { - if((typeof tinymce != 'undefined') && tinymce.EditorManager) { - var id; - for(id in tinymce.EditorManager.editors) { - tinymce.EditorManager.editors[id].remove(); - } - tinymce.EditorManager.editors = {}; - } - }, - - /** - * Function: _loadResponse - * - * Parameters: - * {String} data - Either HTML for straight insertion, or eval'ed JavaScript. - * If passed as HTML, it is assumed that everying inside the tag is replaced, - * but the old tag itself stays intact. - * {String} status - * {XMLHTTPRequest} xmlhttp - .. - * {Array} origData - The original submitted data, useful to do comparisons of changed - * values in new form output, e.g. to detect a URLSegment being changed on the serverside. - * Array in jQuery serializeArray() notation. - */ - _loadResponse: function(data, status, xmlhttp, origData) { - if(status == 'success') { - this.cleanup(); - - var html = data; - - // Rewrite # links - html = html.replace(/(]+href *= *")#/g, '$1' + window.location.href.replace(/#.*$/,'') + '#'); - - // Rewrite iframe links (for IE) - html = html.replace(/(]*src=")([^"]+)("[^>]*>)/g, '$1' + $('base').attr('href') + '$2$3'); - - // Prepare iframes for removal, otherwise we get loading bugs - this.find('iframe').each(function() { - this.contentWindow.location.href = 'about:blank'; - $(this).remove(); - }); - - // update form content - if(html) { - this.html(html); - } else { - this.removeForm(); - } - - // @todo Coupling to avoid FOUC (entwine applies to late) - this.find('.ss-tabset').tabs(); - - this._setupChangeTracker(); - - // Optionally get the form attributes from embedded fields, see Form->formHtmlContent() - for(var overrideAttr in {'action':true,'method':true,'enctype':true,'name':true}) { - var el = this.find(':input[name='+ '_form_' + overrideAttr + ']'); - if(el) { - this.attr(overrideAttr, el.val()); - el.remove(); - } - } - - Behaviour.apply(); // refreshes ComplexTableField - - // focus input on first form element - this.find(':input:visible:first').focus(); - - this.trigger('loadnewpage', {data: data, origData: origData}); - } - - // set status message based on response - var _statusMessage = (xmlhttp.getResponseHeader('X-Status')) ? xmlhttp.getResponseHeader('X-Status') : xmlhttp.statusText; - if(this.hasClass('validationerror')) { - // TODO validation shouldnt need a special case - statusMessage(ss.i18n._t('ModelAdmin.VALIDATIONERROR', 'Validation Error'), 'bad'); - } - } - }); - - /** - * Class: #Form_EditForm .Actions :submit - * - * All buttons in the right CMS form go through here by default. - * We need this onclick overloading because we can't get to the - * clicked button from a form.onsubmit event. - */ - $('#Form_EditForm .Actions :submit').entwine({ - - /** - * Function: onclick - */ - onclick: function(e) { - jQuery('#Form_EditForm').entwine('ss').ajaxSubmit(this); - return false; - } - }); - - /** - * Class: #Form_EditForm textarea.htmleditor - * - * Add tinymce to HtmlEditorFields within the CMS. - */ - $('#Form_EditForm textarea.htmleditor').entwine({ - - /** - * Constructor: onmatch - */ - onmatch : function() { - tinyMCE.execCommand("mceAddControl", true, this.attr('id')); - this.isChanged = function() { - return tinyMCE.getInstanceById(this.attr('id')).isDirty(); - }; - this.resetChanged = function() { - var inst = tinyMCE.getInstanceById(this.attr('id')); - if (inst) inst.startContent = tinymce.trim(inst.getContent({format : 'raw', no_events : 1})); - }; - } - }); - }); -}(jQuery)); \ No newline at end of file diff --git a/javascript/LeftAndMain.Tree.js b/javascript/LeftAndMain.Tree.js deleted file mode 100755 index 04727ede..00000000 --- a/javascript/LeftAndMain.Tree.js +++ /dev/null @@ -1,302 +0,0 @@ -/** - * File: LeftAndMain.Tree.js - */ - -(function($) { - - $.entwine('ss', function($){ - - $('#sitetree_ul').entwine({ - onmatch: function() { - this._super(); - - /** - * @todo Icon and page type hover support - * @todo Sorting of sub nodes (originally placed in context menu) - * @todo Refresh after language - -

    -
    - -
    -
    -
    -

    test 6

    -

    Suspendisse vestibulum dignissim quam. Integer vel augue. Phasellus nulla purus, interdum ac, venenatis non, varius rutrum, leo. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis a eros. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Fusce magna mi, porttitor quis, convallis eget, sodales ac, urna. Phasellus luctus venenatis magna. Vivamus eget lacus. Nunc tincidunt convallis tortor. Duis eros mi, dictum vel, fringilla sit amet, fermentum id, sem. Phasellus nunc enim, faucibus ut, laoreet in, consequat id, metus. Vivamus dignissim. Cras lobortis tempor velit. Phasellus nec diam ac nisl lacinia tristique. Nullam nec metus id mi dictum dignissim. Nullam quis wisi non sem lobortis condimentum. Phasellus pulvinar, nulla non aliquam eleifend, tortor wisi scelerisque felis, in sollicitudin arcu ante lacinia leo.

    -

    -Suspendisse vestibulum dignissim quam. Integer vel augue. Phasellus nulla purus, interdum ac, venenatis non, varius rutrum, leo. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis a eros. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Fusce magna mi, porttitor quis, convallis eget, sodales ac, urna. Phasellus luctus venenatis magna. Vivamus eget lacus. Nunc tincidunt convallis tortor. Duis eros mi, dictum vel, fringilla sit amet, fermentum id, sem. Phasellus nunc enim, faucibus ut, laoreet in, consequat id, metus. Vivamus dignissim. Cras lobortis tempor velit. Phasellus nec diam ac nisl lacinia tristique. Nullam nec metus id mi dictum dignissim. Nullam quis wisi non sem lobortis condimentum. Phasellus pulvinar, nulla non aliquam eleifend, tortor wisi scelerisque felis, in sollicitudin arcu ante lacinia leo.

    -

    -Suspendisse vestibulum dignissim quam. Integer vel augue. Phasellus nulla purus, interdum ac, venenatis non, varius rutrum, leo. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis a eros. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Fusce magna mi, porttitor quis, convallis eget, sodales ac, urna. Phasellus luctus venenatis magna. Vivamus eget lacus. Nunc tincidunt convallis tortor. Duis eros mi, dictum vel, fringilla sit amet, fermentum id, sem. Phasellus nunc enim, faucibus ut, laoreet in, consequat id, metus. Vivamus dignissim. Cras lobortis tempor velit. Phasellus nec diam ac nisl lacinia tristique. Nullam nec metus id mi dictum dignissim. Nullam quis wisi non sem lobortis condimentum. Phasellus pulvinar, nulla non aliquam eleifend, tortor wisi scelerisque felis, in sollicitudin arcu ante lacinia leo.

    -
    -
    - - \ No newline at end of file diff --git a/javascript/ssui.core.js b/javascript/ssui.core.js deleted file mode 100644 index fd5e067b..00000000 --- a/javascript/ssui.core.js +++ /dev/null @@ -1,93 +0,0 @@ -(function($) { - $.widget("ssui.titlebar", { - _create: function() { - this.originalTitle = this.element.attr('title'); - - var self = this; - var options = this.options; - - var title = options.title || this.originalTitle || ' '; - var titleId = $.ui.dialog.getTitleId(this.element); - - this.element.parent().addClass('ui-dialog'); - - var uiDialogTitlebar = this.element. - addClass( - 'ui-dialog-titlebar ' + - 'ui-widget-header ' + - 'ui-corner-all ' + - 'ui-helper-clearfix' - ); - - // By default, the - - if(options.closeButton) { - var uiDialogTitlebarClose = $('') - .addClass( - 'ui-dialog-titlebar-close ' + - 'ui-corner-all' - ) - .attr('role', 'button') - .hover( - function() { - uiDialogTitlebarClose.addClass('ui-state-hover'); - }, - function() { - uiDialogTitlebarClose.removeClass('ui-state-hover'); - } - ) - .focus(function() { - uiDialogTitlebarClose.addClass('ui-state-focus'); - }) - .blur(function() { - uiDialogTitlebarClose.removeClass('ui-state-focus'); - }) - .mousedown(function(ev) { - ev.stopPropagation(); - }) - .appendTo(uiDialogTitlebar); - - var uiDialogTitlebarCloseText = (this.uiDialogTitlebarCloseText = $('')) - .addClass( - 'ui-icon ' + - 'ui-icon-closethick' - ) - .text(options.closeText) - .appendTo(uiDialogTitlebarClose); - } - - var uiDialogTitle = $('') - .addClass('ui-dialog-title') - .attr('id', titleId) - .html(title) - .prependTo(uiDialogTitlebar); - - uiDialogTitlebar.find("*").add(uiDialogTitlebar).disableSelection(); - }, - - destroy: function() { - this.element - .unbind('.dialog') - .removeData('dialog') - .removeClass('ui-dialog-content ui-widget-content') - .hide().appendTo('body'); - - (this.originalTitle && this.element.attr('title', this.originalTitle)); - } - }); - - $.extend($.ssui.titlebar, { - version: "0.0.1", - options: { - title: '', - closeButton: false, - closeText: 'close' - }, - - uuid: 0, - - getTitleId: function($el) { - return 'ui-dialog-title-' + ($el.attr('id') || ++this.uuid); - } - }); -}(jQuery)); \ No newline at end of file diff --git a/templates/Includes/CMSTopMenu.ss b/templates/Includes/CMSTopMenu.ss deleted file mode 100644 index 35fbd5c1..00000000 --- a/templates/Includes/CMSTopMenu.ss +++ /dev/null @@ -1,10 +0,0 @@ - - diff --git a/templates/Includes/Editor_toolbar.ss b/templates/Includes/Editor_toolbar.ss deleted file mode 100755 index bc991b18..00000000 --- a/templates/Includes/Editor_toolbar.ss +++ /dev/null @@ -1,35 +0,0 @@ -<% control EditorToolbar %> -
    - - - - - - -
    - - -<% control Buttons %> - <% if Type = button %> - - $Title - - <% else_if Type = dropdown %> - - <% else_if Type = separator %> - | - <% else_if Type = break %> -
    - <% end_if %> -<% end_control %> - -
    - -
    -
    - -<% end_control %> diff --git a/templates/Includes/LeftAndMain_right.ss b/templates/Includes/LeftAndMain_right.ss deleted file mode 100755 index 362c4cc7..00000000 --- a/templates/Includes/LeftAndMain_right.ss +++ /dev/null @@ -1,3 +0,0 @@ -$EditForm - -
    \ No newline at end of file diff --git a/templates/Includes/LeftAndMain_rightbottom.ss b/templates/Includes/LeftAndMain_rightbottom.ss deleted file mode 100644 index e69de29b..00000000 diff --git a/templates/Includes/MemberTableField.ss b/templates/Includes/MemberTableField.ss deleted file mode 100755 index fa8175d3..00000000 --- a/templates/Includes/MemberTableField.ss +++ /dev/null @@ -1,62 +0,0 @@ -
    -
    - $SearchForm -
    -
    - <% if Markable %> - <% include TableListField_SelectOptions %> - <% end_if %> - <% include TableListField_PageControls %> - - - - <% if Markable %><% end_if %> - <% control Headings %> - - <% end_control %> - <% if Can(show) %><% end_if %> - <% if Can(edit) %><% end_if %> - <% if Can(delete) %><% end_if %> - - - - <% if Can(inlineadd) %> - - <% if Markable %> - - <% end_if %> - - <% if Markable %><% end_if %> - - <% if Can(show) %><% end_if %> - <% if Can(edit) %><% end_if %> - <% if Can(delete) %><% end_if %> - - - - <% if Items %> - <% control Items %> - <% include TableListField_Item %> - <% end_control %> - <% else %> - - <% if Markable %><% end_if %> - - <% control Actions %><% end_control %> - - <% end_if %> - -
     $Title   
     <% end_if %> - $AddRecordForm.CellFields - $AddRecordForm.CellActions
      - - - add<% _t('ADDNEW','Add new',50,'Followed by a member type') %> $Title -    
     <% _t('NOITEMSFOUND', 'No items found') %> 
    -
    - <% control Utility %> - $Title - <% end_control %> -
    -
    -
    \ No newline at end of file diff --git a/templates/Includes/ModelAdmin.ss b/templates/Includes/ModelAdmin.ss deleted file mode 100644 index e69de29b..00000000 diff --git a/templates/Includes/ModelAdmin_ImportSpec.ss b/templates/Includes/ModelAdmin_ImportSpec.ss deleted file mode 100644 index d79b40bb..00000000 --- a/templates/Includes/ModelAdmin_ImportSpec.ss +++ /dev/null @@ -1,21 +0,0 @@ -
    - <% sprintf(_t('IMPORTSPECLINK', 'Show Specification for %s'),$ModelName) %> -
    -

    <% sprintf(_t('IMPORTSPECTITLE', 'Specification for %s'),$ModelName) %>

    -
    <% _t('IMPORTSPECFIELDS', 'Database columns') %>
    - <% control Fields %> -
    -
    $Name
    -
    $Description
    -
    - <% end_control %> - -
    <% _t('IMPORTSPECRELATIONS', 'Relations') %>
    - <% control Relations %> -
    -
    $Name
    -
    $Description
    -
    - <% end_control %> -
    -
    \ No newline at end of file diff --git a/templates/Includes/ModelAdmin_Results.ss b/templates/Includes/ModelAdmin_Results.ss deleted file mode 100644 index a1882ae6..00000000 --- a/templates/Includes/ModelAdmin_Results.ss +++ /dev/null @@ -1,5 +0,0 @@ -<% if Results %> - $Form -<% else %> -

    <% sprintf(_t('ModelAdmin.NORESULTS', 'No results'), $ModelPluralName) %>

    -<% end_if %> \ No newline at end of file diff --git a/templates/Includes/ModelAdmin_left.ss b/templates/Includes/ModelAdmin_left.ss deleted file mode 100755 index c73d0f88..00000000 --- a/templates/Includes/ModelAdmin_left.ss +++ /dev/null @@ -1,26 +0,0 @@ -
    - <% if SearchClassSelector = tabs %> -
      - <% control ModelForms %> -
    • $Title
    • - <% end_control %> -
    - <% end_if %> - - <% if SearchClassSelector = dropdown %> -

    - Search for: - -

    - <% end_if %> - - <% control ModelForms %> -
    - $Content -
    - <% end_control %> -
    \ No newline at end of file diff --git a/templates/Includes/SecurityAdmin_left.ss b/templates/Includes/SecurityAdmin_left.ss deleted file mode 100644 index e031b541..00000000 --- a/templates/Includes/SecurityAdmin_left.ss +++ /dev/null @@ -1,45 +0,0 @@ -
    -

    - <% _t('SECGROUPS','Security Groups') %> -

    - -
    -
    - - - -
    - $AddForm -
    - -
    - $BatchActionsForm -
    - -
    - -
    - - -
    - -
    - $SiteTreeAsUL -
    - -
    - -
    \ No newline at end of file diff --git a/templates/LeftAndMain.ss b/templates/LeftAndMain.ss deleted file mode 100644 index 19b80708..00000000 --- a/templates/LeftAndMain.ss +++ /dev/null @@ -1,60 +0,0 @@ - - - - - -<% base_tag %> -$ApplicationName | $SectionTitle - - - -
    - -
    - -
    - $CMSTopMenu -
    - -
    - $Left -
    - - - -
    - <% control EditorToolbar %> - $ImageForm - $LinkForm - $FlashForm - <% end_control %> -
    - -
    -
    -
    - $ApplicationName - <% if CMSVersion %>-  - $ApplicationName <% _t('APPVERSIONTEXT2',"version that you are currently running, technically it's the CVS branch") %>">$CMSVersion - <% end_if %> -    - <% control CurrentMember %> - <% _t('LOGGEDINAS','Logged in as') %> <% if FirstName && Surname %>$FirstName $Surname<% else_if FirstName %>$FirstName<% else %>$Email<% end_if %> | <% _t('EDITPROFILE','Profile') %> | <% _t('LOGOUT','Log out') %> - <% end_control %> -
    - -
    - <% if ShowSwitchView %> -
    <% _t('VIEWPAGEIN','Page view:') %>
    - $SwitchView - <% end_if %> -
    -
    -
    - - diff --git a/templates/LeftAndMain_printable.ss b/templates/LeftAndMain_printable.ss deleted file mode 100755 index a179464a..00000000 --- a/templates/LeftAndMain_printable.ss +++ /dev/null @@ -1,13 +0,0 @@ - - - - - -<% base_tag %> -SilverStripe CMS - $Title - - - - $PrintForm - - \ No newline at end of file diff --git a/templates/ModelSidebar.ss b/templates/ModelSidebar.ss deleted file mode 100644 index 04092995..00000000 --- a/templates/ModelSidebar.ss +++ /dev/null @@ -1,12 +0,0 @@ -<% if CreateForm %> -

    <% _t('ADDLISTING','Add') %>

    - $CreateForm -<% end_if %> - -

    <% _t('SEARCHLISTINGS','Search') %>

    -$SearchForm - -<% if ImportForm %> -

    <% _t('IMPORT_TAB_HEADER', 'Import') %>

    - $ImportForm -<% end_if %> \ No newline at end of file diff --git a/tests/CMSMenuTest.php b/tests/CMSMenuTest.php deleted file mode 100644 index ef3387e9..00000000 --- a/tests/CMSMenuTest.php +++ /dev/null @@ -1,79 +0,0 @@ -assertTrue((empty($menuItems)), 'Menu can be cleared'); - - // Add a controller to the menu and check it is as expected - CMSMenu::add_controller('CMSMain'); - $menuItems = CMSMenu::get_menu_items(); - $menuItem = $menuItems['CMSMain']; - $this->assertType('CMSMenuItem', $menuItem, 'Controller menu item is of class CMSMenuItem'); - $this->assertEquals($menuItem->url, singleton('CMSMain')->Link(), 'Controller menu item has the correct link'); - $this->assertEquals($menuItem->controller, 'CMSMain', 'Controller menu item has the correct controller class'); - $this->assertEquals($menuItem->priority, singleton('CMSMain')->stat('menu_priority'), 'Controller menu item has the correct priority'); - CMSMenu::clear_menu(); - - // Add a link to the menu - CMSMenu::add_link('LinkCode', 'link title', 'http://www.example.com'); - $menuItems = CMSMenu::get_menu_items(); - $menuItem = $menuItems['LinkCode']; - $this->assertType('CMSMenuItem', $menuItem, 'Link menu item is of class CMSMenuItem'); - $this->assertEquals($menuItem->title, 'link title', 'Link menu item has the correct title'); - $this->assertEquals($menuItem->url,'http://www.example.com', 'Link menu item has the correct link'); - $this->assertNull($menuItem->controller, 'Link menu item has no controller class'); - $this->assertEquals($menuItem->priority, -1, 'Link menu item has the correct priority'); - CMSMenu::clear_menu(); - - } - - public function testCmsClassDetection() { - - // Get CMS classes and check that: - // 1.) CMSMain is included - // 2.) LeftAndMain & ModelAdmin are excluded - $cmsClasses = CMSMenu::get_cms_classes(); - $this->assertContains('CMSMain', $cmsClasses, 'CMSMain included in valid CMS Classes'); - $this->assertNotContains('LeftAndMain', $cmsClasses, 'LeftAndMain not included in valid CMS Classes'); - $this->assertNotContains('ModelAdmin', $cmsClasses, 'LeftAndMain not included in valid CMS Classes'); - - } - - public function testAdvancedMenuHandling() { - - // Populate from CMS Classes, check for existance of CMSMain - CMSMenu::clear_menu(); - CMSMenu::populate_menu(); - $menuItem = CMSMenu::get_menu_item('CMSMain'); - $this->assertType('CMSMenuItem', $menuItem, 'CMSMain menu item exists'); - $this->assertEquals($menuItem->url, singleton('CMSMain')->Link(), 'Menu item has the correct link'); - $this->assertEquals($menuItem->controller, 'CMSMain', 'Menu item has the correct controller class'); - $this->assertEquals( - $menuItem->priority, - singleton('CMSMain')->stat('menu_priority'), - 'Menu item has the correct priority' - ); - - // Check that menu order is correct by priority - // Note this will break if populate_menu includes normal links (ie, as not controller) - $menuItems = CMSMenu::get_menu_items(); - $priority = 9999; // ok, *could* be set larger, but shouldn't need to be! - foreach($menuItems as $menuItem) { - $this->assertEquals( - $menuItem->priority, - singleton($menuItem->controller)->stat('menu_priority'), - "Menu item $menuItem->title has the correct priority" - ); - $this->assertLessThanOrEqual($priority, $menuItem->priority, 'Menu item is of lower or equal priority'); - } - } - -} \ No newline at end of file diff --git a/tests/LeftAndMainTest.php b/tests/LeftAndMainTest.php deleted file mode 100644 index 8395cf0f..00000000 --- a/tests/LeftAndMainTest.php +++ /dev/null @@ -1,176 +0,0 @@ -autoFollowRedirection = false; - CMSMenu::populate_menu(); - } - - public function testSaveTreeNodeSorting() { - $this->loginWithPermission('ADMIN'); - - $rootPages = DataObject::get('SiteTree', '"ParentID" = 0'); // implicitly sorted - $siblingIDs = $rootPages->column('ID'); - $page1 = $rootPages->offsetGet(0); - $page2 = $rootPages->offsetGet(1); - $page3 = $rootPages->offsetGet(2); - - // Move page2 before page1 - $siblingIDs[0] = $page2->ID; - $siblingIDs[1] = $page1->ID; - $data = array( - 'SiblingIDs' => $siblingIDs, - 'ID' => $page2->ID, - 'ParentID' => 0 - ); - - $response = $this->post('admin/savetreenode', $data); - $this->assertEquals(200, $response->getStatusCode()); - $page1 = DataObject::get_by_id('SiteTree', $page1->ID, false); - $page2 = DataObject::get_by_id('SiteTree', $page2->ID, false); - $page3 = DataObject::get_by_id('SiteTree', $page3->ID, false); - - $this->assertEquals(2, $page1->Sort, 'Page1 is sorted after Page2'); - $this->assertEquals(1, $page2->Sort, 'Page2 is sorted before Page1'); - $this->assertEquals(3, $page3->Sort, 'Sort order for other pages is unaffected'); - } - - public function testSaveTreeNodeParentID() { - $this->loginWithPermission('ADMIN'); - - $page1 = $this->objFromFixture('Page', 'page1'); - $page2 = $this->objFromFixture('Page', 'page2'); - $page3 = $this->objFromFixture('Page', 'page3'); - $page31 = $this->objFromFixture('Page', 'page31'); - $page32 = $this->objFromFixture('Page', 'page32'); - - // Move page2 into page3, between page3.1 and page 3.2 - $siblingIDs = array( - $page31->ID, - $page2->ID, - $page32->ID - ); - $data = array( - 'SiblingIDs' => $siblingIDs, - 'ID' => $page2->ID, - 'ParentID' => $page3->ID - ); - $response = $this->post('admin/savetreenode', $data); - $this->assertEquals(200, $response->getStatusCode()); - $page2 = DataObject::get_by_id('SiteTree', $page2->ID, false); - $page31 = DataObject::get_by_id('SiteTree', $page31->ID, false); - $page32 = DataObject::get_by_id('SiteTree', $page32->ID, false); - - $this->assertEquals($page3->ID, $page2->ParentID, 'Moved page gets new parent'); - $this->assertEquals(1, $page31->Sort, 'Children pages before insertaion are unaffected'); - $this->assertEquals(2, $page2->Sort, 'Moved page is correctly sorted'); - $this->assertEquals(3, $page32->Sort, 'Children pages after insertion are resorted'); - } - - /** - * Test that CMS versions can be interpreted appropriately - */ - public function testCMSVersion() { - $l = new LeftAndMain(); - $this->assertEquals("2.4", $l->versionFromVersionFile( - '$URL: http://svn.silverstripe.com/open/modules/cms/branches/2.4/silverstripe_version $')); - $this->assertEquals("2.2.0", $l->versionFromVersionFile( - '$URL: http://svn.silverstripe.com/open/modules/cms/tags/2.2.0/silverstripe_version $')); - $this->assertEquals("trunk", $l->versionFromVersionFile( - '$URL: http://svn.silverstripe.com/open/modules/cms/trunk/silverstripe_version $')); - $this->assertEquals("2.4.0-alpha1", $l->versionFromVersionFile( - '$URL: http://svn.silverstripe.com/open/modules/cms/tags/alpha/2.4.0-alpha1/silverstripe_version $')); - $this->assertEquals("2.4.0-beta1", $l->versionFromVersionFile( - '$URL: http://svn.silverstripe.com/open/modules/cms/tags/beta/2.4.0-beta1/silverstripe_version $')); - $this->assertEquals("2.4.0-rc1", $l->versionFromVersionFile( - '$URL: http://svn.silverstripe.com/open/modules/cms/tags/rc/2.4.0-rc1/silverstripe_version $')); - } - - /** - * Check that all subclasses of leftandmain can be accessed - */ - public function testLeftAndMainSubclasses() { - $adminuser = $this->objFromFixture('Member','admin'); - $this->session()->inst_set('loggedInAs', $adminuser->ID); - - $menuItems = singleton('CMSMain')->MainMenu(); - foreach($menuItems as $menuItem) { - $link = $menuItem->Link; - - // don't test external links - if(preg_match('/^https?:\/\//',$link)) continue; - - $response = $this->get($link); - - $this->assertType('SS_HTTPResponse', $response, "$link should return a response object"); - $this->assertEquals(200, $response->getStatusCode(), "$link should return 200 status code"); - // Check that a HTML page has been returned - $this->assertRegExp('/]*>/i', $response->getBody(), "$link should contain tag"); - $this->assertRegExp('/]*>/i', $response->getBody(), "$link should contain tag"); - $this->assertRegExp('/]*>/i', $response->getBody(), "$link should contain tag"); - } - - $this->session()->inst_set('loggedInAs', null); - - } - - function testCanView() { - $adminuser = $this->objFromFixture('Member', 'admin'); - $assetsonlyuser = $this->objFromFixture('Member', 'assetsonlyuser'); - $allcmssectionsuser = $this->objFromFixture('Member', 'allcmssectionsuser'); - - // anonymous user - $this->session()->inst_set('loggedInAs', null); - $menuItems = singleton('LeftAndMain')->MainMenu(); - $this->assertEquals( - $menuItems->column('Code'), - array(), - 'Without valid login, members cant access any menu entries' - ); - - // restricted cms user - $this->session()->inst_set('loggedInAs', $assetsonlyuser->ID); - $menuItems = singleton('LeftAndMain')->MainMenu(); - $this->assertEquals( - $menuItems->column('Code'), - array('AssetAdmin','Help'), - 'Groups with limited access can only access the interfaces they have permissions for' - ); - - // all cms sections user - $this->session()->inst_set('loggedInAs', $allcmssectionsuser->ID); - $menuItems = singleton('LeftAndMain')->MainMenu(); - $requiredSections = array('CMSMain','AssetAdmin','SecurityAdmin','Help'); - $this->assertEquals( - array_diff($requiredSections, $menuItems->column('Code')), - array(), - 'Group with CMS_ACCESS_LeftAndMain permission can access all sections' - ); - - // admin - $this->session()->inst_set('loggedInAs', $adminuser->ID); - $menuItems = singleton('LeftAndMain')->MainMenu(); - $this->assertContains( - 'CMSMain', - $menuItems->column('Code'), - 'Administrators can access CMS' - ); - $this->assertContains( - 'AssetAdmin', - $menuItems->column('Code'), - 'Administrators can access Assets' - ); - - $this->session()->inst_set('loggedInAs', null); - } - -} - diff --git a/tests/MemberTableFieldTest.php b/tests/MemberTableFieldTest.php deleted file mode 100644 index e5e9694b..00000000 --- a/tests/MemberTableFieldTest.php +++ /dev/null @@ -1,139 +0,0 @@ -objFromFixture('Member', 'member1'); - $member2 = $this->objFromFixture('Member', 'member2'); - $member3 = $this->objFromFixture('Member', 'member3'); - $group1 = $this->objFromFixture('Group', 'group1'); - - $tf = new MemberTableField( - $this, - "Members", - $group1 - ); - $members = $tf->sourceItems(); - - $this->assertContains($member1->ID, $members->column('ID'), - 'Members in the associated group are listed' - ); - $this->assertContains($member2->ID, $members->column('ID'), - 'Members in children groups are listed as well' - ); - $this->assertNotContains($member3->ID, $members->column('ID'), - 'Members in other groups are filtered out' - ); - } - - function testShowsAllMembersWithoutGroupParameter() { - $member1 = $this->objFromFixture('Member', 'member1'); - $member2 = $this->objFromFixture('Member', 'member2'); - $member3 = $this->objFromFixture('Member', 'member3'); - $group1 = $this->objFromFixture('Group', 'group1'); - - $tf = new MemberTableField( - $this, - "Members" - // no group assignment - ); - $members = $tf->sourceItems(); - - $this->assertContains($member1->ID, $members->column('ID'), - 'Members in the associated group are listed' - ); - $this->assertContains($member2->ID, $members->column('ID'), - 'Members in children groups are listed as well' - ); - $this->assertContains($member3->ID, $members->column('ID'), - 'Members in other groups are listed' - ); - } - - function testDeleteWithGroupOnlyDeletesRelation() { - $member1 = $this->objFromFixture('Member', 'member1'); - $group1 = $this->objFromFixture('Group', 'group1'); - - $response = $this->get('MemberTableFieldTest_Controller'); - $token = SecurityToken::inst(); - $url = sprintf('MemberTableFieldTest_Controller/Form/field/Members/item/%d/delete/?usetestmanifest=1', $member1->ID); - $url = $token->addToUrl($url); - $response = $this->get($url); - - $group1->flushCache(); - - $this->assertNotContains($member1->ID, $group1->Members()->column('ID'), - 'Member relation to group is removed' - ); - $this->assertType( - 'DataObject', - DataObject::get_by_id('Member', $member1->ID), - 'Member record still exists' - ); - } - - function testDeleteWithoutGroupDeletesFromDatabase() { - $member1 = $this->objFromFixture('Member', 'member1'); - $member1ID = $member1->ID; - $group1 = $this->objFromFixture('Group', 'group1'); - - $response = $this->get('MemberTableFieldTest_Controller'); - $token = SecurityToken::inst(); - $url = sprintf('MemberTableFieldTest_Controller/FormNoGroup/field/Members/item/%d/delete/?usetestmanifest=1', $member1->ID); - $url = $token->addToUrl($url); - $response = $this->get($url); - - $group1->flushCache(); - - $this->assertNotContains($member1->ID, $group1->Members()->column('ID'), - 'Member relation to group is removed' - ); - DataObject::flush_and_destroy_cache(); - $this->assertFalse( - DataObject::get_by_id('Member', $member1ID), - 'Member record is removed from database' - ); - } -} - -class MemberTableFieldTest_Controller extends Controller implements TestOnly { - - protected $template = 'BlankPage'; - - function Link($action = null) { - return Controller::join_links('MemberTableFieldTest_Controller', $action); - } - - function Form() { - $group1 = DataObject::get_one('Group', '"Code" = \'group1\''); - return new Form( - $this, - 'FormNoGroup', - new FieldSet(new MemberTableField($this, "Members", $group1)), - new FieldSet(new FormAction('submit')) - ); - } - - function FormNoGroup() { - $tf = new MemberTableField( - $this, - "Members" - // no group - ); - - return new Form( - $this, - 'FormNoGroup', - new FieldSet(new MemberTableField($this, "Members")), - new FieldSet(new FormAction('submit')) - ); - } - -} \ No newline at end of file diff --git a/tests/MemberTableFieldTest.yml b/tests/MemberTableFieldTest.yml deleted file mode 100644 index bc9785f2..00000000 --- a/tests/MemberTableFieldTest.yml +++ /dev/null @@ -1,31 +0,0 @@ -Group: - admin: - Title: Administrators - Code: admin - group1: - Title: Group1 - Code: group1 - group1_child: - Title: Group1 Child - Parent: =>Group.group1 - Code: group1_child - group2: - Title: Group2 - Code: group2 -Member: - admin: - Email: admin@example.com - Groups: =>Group.admin - member1: - Email: member1@test.com - Groups: =>Group.group1 - member2: - Email: member2@test.com - Groups: =>Group.group1_child - member3: - Email: member3@test.com - Groups: =>Group.group2 -Permission: - admin: - Code: ADMIN - GroupID: =>Group.admin \ No newline at end of file diff --git a/tests/ModelAdminTest.php b/tests/ModelAdminTest.php deleted file mode 100644 index 7db110eb..00000000 --- a/tests/ModelAdminTest.php +++ /dev/null @@ -1,32 +0,0 @@ -autoFollowRedirection = false; - $this->logInAs('admin'); - $this->assertTrue((bool)Permission::check("ADMIN")); - $this->assertEquals(200, $this->get('ModelAdminTest_Admin')->getStatusCode()); - } -} - -class ModelAdminTest_Admin extends ModelAdmin implements TestOnly { - static $url_segment = 'testadmin'; - - public static $managed_models = array( - 'ModelAdminTest_Contact', - ); -} - -class ModelAdminTest_Contact extends DataObject implements TestOnly { - static $db = array( - "Name" => "Varchar", - "Phone" => "Varchar", - ); -} \ No newline at end of file diff --git a/tests/ModelAdminTest.yml b/tests/ModelAdminTest.yml deleted file mode 100644 index 1a1758b5..00000000 --- a/tests/ModelAdminTest.yml +++ /dev/null @@ -1,19 +0,0 @@ -ModelAdminTest_Contact: - sam: - Name: Sam - Phone: 021 123 456 - ingo: - Name: ingo - Phone: 04 987 6543 - -Member: - admin: - FirstName: admin -Group: - admin: - Title: Admin - Members: =>Member.admin -Permission: - admin: - Code: ADMIN - Group: =>Group.admin \ No newline at end of file diff --git a/tests/SecurityAdminTest.php b/tests/SecurityAdminTest.php deleted file mode 100644 index 796502a3..00000000 --- a/tests/SecurityAdminTest.php +++ /dev/null @@ -1,86 +0,0 @@ -session()->inst_set('loggedInAs', $this->idFromFixture('Member', 'admin')); - - /* First, open the applicable group */ - $this->get('admin/security/show/' . $this->idFromFixture('Group','admin')); - $this->assertRegExp('/]+id="Form_EditForm_Title"[^>]+value="Administrators"[^>]*>/',$this->content()); - - /* Then load the export page */ - $this->get('admin/security/EditForm/field/Members/export'); - $lines = preg_split('/\n/', $this->content()); - - $this->assertEquals(count($lines), 3, "Export with members has one content row"); - $this->assertRegExp('/"","","admin@example.com"/', $lines[1], "Member values are correctly exported"); - } - - function testEmptyGroupExport() { - $this->session()->inst_set('loggedInAs', $this->idFromFixture('Member', 'admin')); - - /* First, open the applicable group */ - $this->get('admin/security/show/' . $this->idFromFixture('Group','empty')); - $this->assertRegExp('/]+id="Form_EditForm_Title"[^>]+value="Empty Group"[^>]*>/',$this->content()); - - /* Then load the export page */ - $this->get('admin/security/EditForm/field/Members/export'); - $lines = preg_split('/\n/', $this->content()); - - $this->assertEquals(count($lines), 2, "Empty export only has header fields and an empty row"); - $this->assertEquals($lines[1], '', "Empty export only has no content row"); - } - - function testAddHiddenPermission() { - SecurityAdmin::add_hidden_permission('CMS_ACCESS_ReportAdmin'); - $this->assertContains('CMS_ACCESS_ReportAdmin', SecurityAdmin::get_hidden_permissions()); - - // reset to defaults - SecurityAdmin::clear_hidden_permissions(); - } - - function testRemoveHiddenPermission() { - SecurityAdmin::add_hidden_permission('CMS_ACCESS_ReportAdmin'); - $this->assertContains('CMS_ACCESS_ReportAdmin', SecurityAdmin::get_hidden_permissions()); - SecurityAdmin::remove_hidden_permission('CMS_ACCESS_ReportAdmin'); - $this->assertNotContains('CMS_ACCESS_ReportAdmin', SecurityAdmin::get_hidden_permissions()); - - // reset to defaults - SecurityAdmin::clear_hidden_permissions(); - } - - function testClearHiddenPermission() { - SecurityAdmin::add_hidden_permission('CMS_ACCESS_ReportAdmin'); - $this->assertContains('CMS_ACCESS_ReportAdmin', SecurityAdmin::get_hidden_permissions()); - SecurityAdmin::clear_hidden_permissions('CMS_ACCESS_ReportAdmin'); - $this->assertNotContains('CMS_ACCESS_ReportAdmin', SecurityAdmin::get_hidden_permissions()); - } - - function testPermissionFieldRespectsHiddenPermissions() { - $this->session()->inst_set('loggedInAs', $this->idFromFixture('Member', 'admin')); - - $group = $this->objFromFixture('Group', 'admin'); - - SecurityAdmin::add_hidden_permission('CMS_ACCESS_ReportAdmin'); - $response = $this->get('admin/security/show/' . $group->ID); - - $this->assertContains( - 'CMS_ACCESS_CMSMain', - $response->getBody() - ); - $this->assertNotContains( - 'CMS_ACCESS_ReportAdmin', - $response->getBody() - ); - - // reset to defaults - SecurityAdmin::clear_hidden_permissions(); - } -} - -?> \ No newline at end of file diff --git a/thirdparty/jquery-layout/README.txt b/thirdparty/jquery-layout/README.txt deleted file mode 100644 index 7e5f878b..00000000 --- a/thirdparty/jquery-layout/README.txt +++ /dev/null @@ -1 +0,0 @@ -See http://layout.jquery-dev.net/downloads.html \ No newline at end of file diff --git a/thirdparty/jquery-layout/changelog.txt b/thirdparty/jquery-layout/changelog.txt deleted file mode 100755 index 4217cfdf..00000000 --- a/thirdparty/jquery-layout/changelog.txt +++ /dev/null @@ -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 \ No newline at end of file diff --git a/thirdparty/jquery-layout/example.html b/thirdparty/jquery-layout/example.html deleted file mode 100755 index b4ffd66e..00000000 --- a/thirdparty/jquery-layout/example.html +++ /dev/null @@ -1,23 +0,0 @@ - - -Layout Example - - - - - -
    Center -

    Go to the Demos page

    -

    * Pane-resizing is disabled because ui.draggable.js is not linked

    -

    * Pane-animation is disabled because ui.effects.js is not linked

    -
    -
    North
    -
    South
    -
    East
    -
    West
    - - \ No newline at end of file diff --git a/thirdparty/jquery-layout/jquery.js b/thirdparty/jquery-layout/jquery.js deleted file mode 100755 index ff2e2994..00000000 --- a/thirdparty/jquery-layout/jquery.js +++ /dev/null @@ -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 = "
    a"; - - 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 = ""; - - 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 = "
    "; - jQuery.support.shrinkWrapBlocks = div.offsetWidth !== 2; - } - - div.innerHTML = "
    t
    "; - 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 = ""; - - // 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 = ""; - - 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 = "

    "; - - // 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 = "
    "; - - // 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 = /", "" ], - legend: [ 1, "
    ", "
    " ], - thead: [ 1, "", "
    " ], - tr: [ 2, "", "
    " ], - td: [ 3, "", "
    " ], - col: [ 2, "", "
    " ], - area: [ 1, "", "" ], - _default: [ 0, "", "" ] - }; - -wrapMap.optgroup = wrapMap.option; -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -// IE can't serialize and - - - - - - - - - - -
    - -
    - -
    Inner Center
    -
    Inner West
    -
    Inner East
    -
    Inner North
    -
    Inner South
    - -
    -
    Middle West
    -
    Middle East
    - -
    - -
    Outer West
    -
    Outer East
    - -
    Outer North
    -
    Outer South
    - - - - diff --git a/thirdparty/jquery-layout/simple.html b/thirdparty/jquery-layout/simple.html deleted file mode 100755 index c450190d..00000000 --- a/thirdparty/jquery-layout/simple.html +++ /dev/null @@ -1,197 +0,0 @@ - - - - - Simple Layout Demo - - - - - - - - - - - - - - - - - -
    - This is the north pane, closable, slidable and resizable - -
      -
    • -
        -
      • one
      • -
      • two
      • -
      • three
      • -
      • four
      • -
      • five
      • -
      - Drop-Down -
    • -
    - -
    - - -
    - This is the west pane, closable, slidable and resizable - -
      -
    • -
        -
      • one
      • -
      • two
      • -
      • three
      • -
      • four
      • -
      • five
      • -
      - Pop-Up -
    • -
    - -

    - -
    - -
    - This is the south pane, closable, slidable and resizable   - -
    - -
    - This is the east pane, closable, slidable and resizable - - -
      -
    • -
        -
      • one
      • -
      • two
      • -
      • three
      • -
      • four
      • -
      • five
      • -
      - Pop-Up -
    • -
    - -

    - -

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    -

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    -

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    -

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    -
    - -
    - This center pane auto-sizes to fit the space between the 'border-panes' -

    This layout was created with only default options - no customization

    -

    Only the applyDefaultStyles option was enabled for basic formatting

    -

    The Pop-Up and Drop-Down lists demonstrate the allowOverflow() utility

    -

    The Close and Toggle buttons are examples of custom buttons

    -

    Go to the Demos page

    -

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    -

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    -

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    -

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    ...

    -
    - - - \ No newline at end of file diff --git a/thirdparty/jquery-notice/.piston.yml b/thirdparty/jquery-notice/.piston.yml deleted file mode 100644 index 5d5c4851..00000000 --- a/thirdparty/jquery-notice/.piston.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -format: 1 -handler: - piston:remote-revision: 14 - piston:uuid: c0f28f7c-efbc-11dd-aa46-671127399dce -lock: false -repository_url: http://jquery-notice.googlecode.com/svn/trunk/ -repository_class: Piston::Svn::Repository diff --git a/thirdparty/jquery-notice/jquery.notice.css b/thirdparty/jquery-notice/jquery.notice.css deleted file mode 100644 index 7803e2d1..00000000 --- a/thirdparty/jquery-notice/jquery.notice.css +++ /dev/null @@ -1,36 +0,0 @@ -.notice-wrap { - position: fixed; - top: 20px; - right: 20px; - width: 250px; - z-index: 9999; -} - -* html .notice-wrap { - position: absolute; -} - -.notice-item { - height: 60px; - background: #333; - -moz-border-radius: 6px; - -webkit-border-radius: 6px; - color: #eee; - padding: 6px 6px 0 6px; - font-family: lucida Grande; - font-size: 11px; - border: 2px solid #999; - display: block; - position: relative; - margin: 0 0 12px 0; -} - -.notice-item-close { - position: absolute; - font-family: Arial; - font-size: 12px; - font-weight: bold; - right: 6px; - top: 6px; - cursor: pointer; -} diff --git a/thirdparty/jquery-notice/jquery.notice.js b/thirdparty/jquery-notice/jquery.notice.js deleted file mode 100644 index 10cc4cb5..00000000 --- a/thirdparty/jquery-notice/jquery.notice.js +++ /dev/null @@ -1,73 +0,0 @@ -/** -* jQuery.noticeAdd() and jQuery.noticeRemove() -* These functions create and remove growl-like notices -* -* Copyright (c) 2009 Tim Benniks -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. -* -* @author Tim Benniks -* @copyright 2009 timbenniks.com -* @version $Id: jquery.notice.js 1 2009-01-24 12:24:18Z timbenniks $ -**/ -(function(jQuery) -{ - jQuery.extend({ - noticeAdd: function(options) - { - var defaults = { - inEffect: {opacity: 'show'}, // in effect - inEffectDuration: 600, // in effect duration in miliseconds - stayTime: 3000, // time in miliseconds before the item has to disappear - text: '', // content of the item - stay: false, // should the notice item stay or not? - type: 'notice' // could also be error, succes - } - - // declare varaibles - var options, noticeWrapAll, noticeItemOuter, noticeItemInner, noticeItemClose; - - options = jQuery.extend({}, defaults, options); - noticeWrapAll = (!jQuery('.notice-wrap').length) ? jQuery('
    ').addClass('notice-wrap').appendTo('body') : jQuery('.notice-wrap'); - noticeItemOuter = jQuery('
    ').addClass('notice-item-wrapper'); - noticeItemInner = jQuery('
    ').hide().addClass('notice-item ' + options.type).appendTo(noticeWrapAll).html('

    '+options.text+'

    ').animate(options.inEffect, options.inEffectDuration).wrap(noticeItemOuter); - noticeItemClose = jQuery('
    ').addClass('notice-item-close').prependTo(noticeItemInner).html('x').click(function() { jQuery.noticeRemove(noticeItemInner) }); - - if(!options.stay) - { - setTimeout(function() - { - jQuery.noticeRemove(noticeItemInner); - }, - options.stayTime); - } - }, - - noticeRemove: function(obj) - { - obj.animate({opacity: '0'}, 600, function() - { - obj.parent().animate({height: '0px'}, 300, function() - { - obj.parent().remove(); - }); - }); - } - }); -})(jQuery); \ No newline at end of file