Merge branch 'integration'

Conflicts:
	forms/gridfield/GridField.php
This commit is contained in:
Ingo Schommer 2012-03-08 20:25:21 +01:00
commit e4a6dd5850
129 changed files with 3419 additions and 2855 deletions

View File

@ -25,7 +25,7 @@ HtmlEditorConfig::get('cms')->setOptions(array(
'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]" '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('media', 'fullscreen', 'autoresize');
HtmlEditorConfig::get('cms')->enablePlugins(array('ssbuttons' => '../../../cms/javascript/tinymce_ssbuttons/editor_plugin_src.js')); HtmlEditorConfig::get('cms')->enablePlugins(array('ssbuttons' => '../../../cms/javascript/tinymce_ssbuttons/editor_plugin_src.js'));
HtmlEditorConfig::get('cms')->insertButtonsBefore('formatselect', 'styleselect'); HtmlEditorConfig::get('cms')->insertButtonsBefore('formatselect', 'styleselect');

View File

@ -2,6 +2,7 @@
class CMSProfileController extends LeftAndMain { class CMSProfileController extends LeftAndMain {
static $url_segment = 'myprofile'; static $url_segment = 'myprofile';
static $required_permission_codes = false;
public function index($request) { public function index($request) {
$form = $this->Member_ProfileForm(); $form = $this->Member_ProfileForm();

View File

@ -9,7 +9,7 @@
* @package cms * @package cms
* @subpackage core * @subpackage core
*/ */
class LeftAndMain extends Controller { class LeftAndMain extends Controller implements PermissionProvider {
/** /**
* The 'base' url for CMS administration areas. * The 'base' url for CMS administration areas.
@ -77,13 +77,21 @@ class LeftAndMain extends Controller {
'show', 'show',
'EditorToolbar', 'EditorToolbar',
'EditForm', 'EditForm',
'RootForm',
'AddForm', 'AddForm',
'batchactions', 'batchactions',
'BatchActionsForm', 'BatchActionsForm',
'Member_ProfileForm', 'Member_ProfileForm',
); );
/**
* @var Array Codes which are required from the current user to view this controller.
* If multiple codes are provided, all of them are required.
* All CMS controllers require "CMS_ACCESS_LeftAndMain" as a baseline check,
* and fall back to "CMS_ACCESS_<class>" if no permissions are defined here.
* See {@link canView()} for more details on permission checks.
*/
static $required_permission_codes;
/** /**
* Register additional requirements through the {@link Requirements} class. * Register additional requirements through the {@link Requirements} class.
* Used mainly to work around the missing "lazy loading" functionality * Used mainly to work around the missing "lazy loading" functionality
@ -99,13 +107,10 @@ class LeftAndMain extends Controller {
/** /**
* @param Member $member * @param Member $member
*
* @return boolean * @return boolean
*/ */
function canView($member = null) { function canView($member = null) {
if(!$member && $member !== FALSE) { if(!$member && $member !== FALSE) $member = Member::currentUser();
$member = Member::currentUser();
}
// cms menus only for logged-in members // cms menus only for logged-in members
if(!$member) return false; if(!$member) return false;
@ -116,11 +121,17 @@ class LeftAndMain extends Controller {
if($alternateAllowed === FALSE) return false; if($alternateAllowed === FALSE) return false;
} }
// Default security check for LeftAndMain sub-class permissions // Check for "CMS admin" permission
if(!Permission::checkMember($member, "CMS_ACCESS_$this->class") && if(Permission::checkMember($member, "CMS_ACCESS_LeftAndMain")) return true;
!Permission::checkMember($member, "CMS_ACCESS_LeftAndMain")) {
return false; // Check for LeftAndMain sub-class permissions
$codes = array();
$extraCodes = $this->stat('required_permission_codes');
if($extraCodes !== false) { // allow explicit FALSE to disable subclass check
if($extraCodes) $codes = array_merge($codes, (array)$extraCodes);
else $codes[] = "CMS_ACCESS_$this->class";
} }
foreach($codes as $code) if(!Permission::checkMember($member, $code)) return false;
return true; return true;
} }
@ -861,18 +872,18 @@ class LeftAndMain extends Controller {
$actions = $record->getCMSActions(); $actions = $record->getCMSActions();
// add default actions if none are defined // add default actions if none are defined
if(!$actions || !$actions->Count()) { if(!$actions || !$actions->Count()) {
if($record->hasMethod('canDelete') && $record->canDelete()) {
$actions->push(
FormAction::create('delete',_t('ModelAdmin.DELETE','Delete'))
->addExtraClass('ss-ui-action-destructive')
);
}
if($record->hasMethod('canEdit') && $record->canEdit()) { if($record->hasMethod('canEdit') && $record->canEdit()) {
$actions->push( $actions->push(
FormAction::create('save',_t('CMSMain.SAVE','Save')) FormAction::create('save',_t('CMSMain.SAVE','Save'))
->addExtraClass('ss-ui-action-constructive')->setAttribute('data-icon', 'accept') ->addExtraClass('ss-ui-action-constructive')->setAttribute('data-icon', 'accept')
); );
} }
if($record->hasMethod('canDelete') && $record->canDelete()) {
$actions->push(
FormAction::create('delete',_t('ModelAdmin.DELETE','Delete'))
->addExtraClass('ss-ui-action-destructive')
);
}
} }
} }
@ -913,16 +924,12 @@ class LeftAndMain extends Controller {
$form->setFields($readonlyFields); $form->setFields($readonlyFields);
} }
} else { } else {
$form = $this->RootForm(); $form = $this->EmptyForm();
} }
return $form; return $form;
} }
function RootForm() {
return $this->EmptyForm();
}
/** /**
* Returns a placeholder form, used by {@link getEditForm()} if no record is selected. * 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. * Our javascript logic always requires a form to be present in the CMS interface.
@ -1006,10 +1013,7 @@ class LeftAndMain extends Controller {
$form->saveInto($record); $form->saveInto($record);
$record->write(); $record->write();
// Used in TinyMCE inline folder creation if($this->isAjax()) {
if(isset($data['returnID'])) {
return $record->ID;
} else if($this->isAjax()) {
$form = $this->getEditForm($record->ID); $form = $this->getEditForm($record->ID);
return $form->forTemplate(); return $form->forTemplate();
} else { } else {
@ -1084,11 +1088,11 @@ class LeftAndMain extends Controller {
'BatchActionsForm', 'BatchActionsForm',
new FieldList( new FieldList(
new HiddenField('csvIDs'), new HiddenField('csvIDs'),
new DropdownField( Object::create('DropdownField',
'Action', 'Action',
false, false,
$actionsMap $actionsMap
) )->setAttribute('autocomplete', 'off')
), ),
new FieldList( new FieldList(
// TODO i18n // TODO i18n
@ -1311,6 +1315,37 @@ class LeftAndMain extends Controller {
return DBField::create('DBLocale', i18n::get_locale()); return DBField::create('DBLocale', i18n::get_locale());
} }
function providePermissions() {
$perms = array(
"CMS_ACCESS_LeftAndMain" => array(
'name' => _t('CMSMain.ACCESSALLINTERFACES', 'Access to all CMS sections'),
'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access'),
'help' => _t('CMSMain.ACCESSALLINTERFACESHELP', 'Overrules more specific access settings.'),
'sort' => -100
)
);
// Add any custom ModelAdmin subclasses. Can't put this on ModelAdmin itself
// since its marked abstract, and needs to be singleton instanciated.
foreach(ClassInfo::subclassesFor('ModelAdmin') as $i => $class) {
if($class == 'ModelAdmin') continue;
if(ClassInfo::classImplements($class, 'TestOnly')) continue;
$title = _t("{$class}.MENUTITLE", LeftAndMain::menu_title_for_class($class));
$perms["CMS_ACCESS_" . $class] = array(
'name' => sprintf(_t(
'CMSMain.ACCESS',
"Access to '%s' section",
PR_MEDIUM,
"Item in permission selection identifying the admin section. Example: Access to 'Files & Images'"
), $title, null),
'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access')
);
}
return $perms;
}
/** /**
* Register the given javascript file as required in the CMS. * Register the given javascript file as required in the CMS.
* Filenames should be relative to the base, eg, SAPPHIRE_DIR . '/javascript/loader.js' * Filenames should be relative to the base, eg, SAPPHIRE_DIR . '/javascript/loader.js'

View File

@ -1,370 +0,0 @@
<?php
/**
* Enhances {ComplexTableField} with the ability to list groups and given members.
* It is based around groups, so it deletes Members from a Group rather than from the entire system.
*
* In contrast to the original implementation, the URL-parameters "ParentClass" and "ParentID" are used
* to specify "Group" (hardcoded) and the GroupID-relation.
*
* @todo write a better description about what this field does.
*
* Returns either:
* - provided members
* - members of a provided group
* - all members
* - members based on a search-query
*
* @package cms
* @subpackage security
*/
class MemberTableField extends ComplexTableField {
protected $members;
protected $hidePassword;
protected $detailFormValidator;
protected $group;
protected $template = 'MemberTableField';
public $popupClass = 'MemberTableField_Popup';
public $itemClass = 'MemberTableField_Item';
/**
* Set the page size for this table.
* @var int
*/
public static $page_size = 20;
protected $permissions = array(
"add",
"edit",
"show",
"delete",
'inlineadd'
//"export",
);
/**
* Constructor method for MemberTableField.
*
* @param Controller $controller Controller class which created this field
* @param string $name Name of the field (e.g. "Members")
* @param mixed $group Can be the ID of a Group instance, or a Group instance itself
* @param SS_List $members Optional set of Members to set as the source items for this field
* @param boolean $hidePassword Hide the password field or not in the summary?
*/
function __construct($controller, $name, $group = null, $members = null, $hidePassword = true) {
if(!$members) {
if($group) {
if(is_numeric($group)) $group = DataObject::get_by_id('Group', $group);
$this->group = $group;
$members = $group->Members();
} elseif(isset($_REQUEST['ctf'][$this->getName()]["ID"]) && is_numeric($_REQUEST['ctf'][$this->getName()]["ID"])) {
throw new Exception("Is this still being used? It's a hack and we should remove it.");
$group = DataObject::get_by_id('Group', $_REQUEST['ctf'][$this->getName()]["ID"]);
$this->group = $group;
$members = $group->Members();
} else {
$members = DataObject::get("Member");
}
}
$SNG_member = singleton('Member');
$fieldList = $SNG_member->summaryFields();
$memberDbFields = $SNG_member->db();
$csvFieldList = array();
foreach($memberDbFields as $field => $dbFieldType) {
$csvFieldList[$field] = $field;
}
if(!$hidePassword) {
$fieldList["SetPassword"] = "Password";
}
$this->hidePassword = $hidePassword;
// Add a search filter
$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}%'";
}
$members = $members->where('(' . implode(' OR ', $searchFilters) . ')');
}
parent::__construct($controller, $name, $members, $fieldList);
$this->setFieldListCsv($csvFieldList);
$this->setPageSize($this->stat('page_size'));
}
function FieldHolder() {
$ret = parent::FieldHolder();
Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/scriptaculous/controls.js");
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/MemberTableField.js');
Requirements::javascript(SAPPHIRE_ADMIN_DIR . "/javascript/MemberTableField_popup.js");
return $ret;
}
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','<input type="submit" class="action" name="MemberFilterButton" value="'._t('MemberTableField.FILTER', 'Filter').'" id="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 = 'Member';
$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();
$this->getDataList()->add($record);
$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 Functions
* #################################
*/
/**
* @return Group
*/
function getGroup() {
return $this->group;
}
/**
* Add existing member to group by name (with JS-autocompletion)
*/
function AddRecordForm() {
$fields = new FieldList();
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 FieldList(
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(
'<small><a href="' . $_SERVER['HTTP_REFERER'] . '" onclick="javascript:window.top.GB_hide(); return false;">(%s)</a></small>',
_t('ComplexTableField.CLOSEPOPUP', 'Close Popup')
);
$message = sprintf(
_t('ComplexTableField.SUCCESSADD', 'Added %s %s %s'),
$childData->singular_name(),
'<a href="' . $this->Link() . '">' . htmlspecialchars($childData->Title, ENT_QUOTES, 'UTF-8') . '</a>',
$closeLink
);
$form->sessionMessage($message, 'good');
$this->controller->redirectBack();
}
}
/**
* 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->getParentController()->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(SAPPHIRE_ADMIN_DIR . '/css/SecurityAdmin.css');
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/MemberTableField.js');
Requirements::javascript(SAPPHIRE_ADMIN_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;
}
}

View File

@ -125,7 +125,7 @@ abstract class ModelAdmin extends LeftAndMain {
* Class name of the form field used for the results list. Overloading this in subclasses * Class name of the form field used for the results list. Overloading this in subclasses
* can let you customise the results table field. * can let you customise the results table field.
*/ */
protected $resultsTableClassName = 'TableListField'; protected $resultsTableClassName = 'GridField';
/** /**
* Return {@link $this->resultsTableClassName} * Return {@link $this->resultsTableClassName}
@ -651,24 +651,7 @@ class ModelAdmin_CollectionController extends Controller {
function search($request, $form) { function search($request, $form) {
// Get the results form to be rendered // Get the results form to be rendered
$resultsForm = $this->ResultsForm(array_merge($form->getData(), $request)); $resultsForm = $this->ResultsForm(array_merge($form->getData(), $request));
// Before rendering, let's get the total number of results returned return $resultsForm->forTemplate();
$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->forTemplate(),
200,
$msg
);
} }
/** /**
@ -715,36 +698,20 @@ class ModelAdmin_CollectionController extends Controller {
* *
* @param array $searchCriteria passed through from ResultsForm * @param array $searchCriteria passed through from ResultsForm
* *
* @return TableListField * @return GridField
*/ */
function getResultsTable($searchCriteria) { function getResultsTable($searchCriteria) {
$summaryFields = $this->getResultColumns($searchCriteria);
$className = $this->parentController->resultsTableClassName(); $className = $this->parentController->resultsTableClassName();
$tf = new $className( $datalist = $this->getSearchQuery($searchCriteria);
$numItemsPerPage = $this->parentController->stat('page_length');
$tf = Object::create($className,
$this->modelClass, $this->modelClass,
$this->modelClass, false,
$summaryFields $datalist,
); $fieldConfig = GridFieldConfig_RecordEditor::create($numItemsPerPage)
->addComponent(new GridFieldExporter())->removeComponentsByType('GridFieldFilter')
$tf->setCustomQuery($this->getSearchQuery($searchCriteria)); )->setDisplayFields($this->getResultColumns($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 = '<a href=\"' . $this->Link() . '/$ID/edit\">$value</a>';
if(count($summaryFields)) {
$tf->setFieldFormatting(array_combine(
array_keys($summaryFields),
array_fill(0,count($summaryFields), $url)
));
}
return $tf; return $tf;
} }
@ -757,7 +724,6 @@ class ModelAdmin_CollectionController extends Controller {
* @return Form * @return Form
*/ */
function ResultsForm($searchCriteria) { function ResultsForm($searchCriteria) {
if($searchCriteria instanceof SS_HTTPRequest) $searchCriteria = $searchCriteria->getVars(); if($searchCriteria instanceof SS_HTTPRequest) $searchCriteria = $searchCriteria->getVars();
$tf = $this->getResultsTable($searchCriteria); $tf = $this->getResultsTable($searchCriteria);
@ -876,6 +842,13 @@ class ModelAdmin_CollectionController extends Controller {
Director::redirect(Controller::join_links($this->Link(), $model->ID , 'edit')); Director::redirect(Controller::join_links($this->Link(), $model->ID , 'edit'));
} }
} }
/**
* @return ArrayList
*/
public function Breadcrumbs(){
return new ArrayList();
}
} }
/** /**

View File

@ -17,15 +17,11 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
static $subitem_class = 'Member'; static $subitem_class = 'Member';
static $allowed_actions = array( static $allowed_actions = array(
'autocomplete',
'removememberfromgroup',
'AddRecordForm',
'EditForm', 'EditForm',
'MemberImportForm', 'MemberImportForm',
'memberimport', 'memberimport',
'GroupImportForm', 'GroupImportForm',
'groupimport', 'groupimport',
'RootForm'
); );
/** /**
@ -47,71 +43,29 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
$record = $this->getRecord($id); $record = $this->getRecord($id);
if($record && !$record->canView()) return Security::permissionFailure($this); if($record && !$record->canView()) return Security::permissionFailure($this);
if($id && is_numeric($id)) { $memberList = Object::create('GridField',
$form = parent::getEditForm($id); 'Members',
if(!$form) return false; false,
DataList::create('Member'),
$memberListConfig = GridFieldConfig_RecordEditor::create()
->addComponent(new GridFieldExporter())
)->addExtraClass("members_grid");
$memberListConfig->getComponentByType('GridFieldPopupForms')->setValidator(new Member_Validator());
$fields = $form->Fields(); $groupList = Object::create('GridField',
if($fields->hasTabSet() && $record->canEdit()) { 'Groups',
$fields->findOrMakeTab('Root.Import',_t('Group.IMPORTTABTITLE', 'Import')); false,
$fields->addFieldToTab('Root.Import', DataList::create('Group'),
new LiteralField( GridFieldConfig_RecordEditor::create()
'MemberImportFormIframe', )->setDisplayFields(array(
sprintf( 'Breadcrumbs' => singleton('Group')->fieldLabel('Title')
'<iframe src="%s" id="MemberImportFormIframe" width="100%%" height="400px" border="0"></iframe>', ));
$this->Link('memberimport')
)
)
);
if(Permission::check('APPLY_ROLES')) {
$fields->addFieldToTab(
'Root.Roles',
new LiteralField(
'RolesAddEditLink',
sprintf(
'<p class="add-role"><a href="%s">%s</a></p>',
$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')
)
)
);
}
// Filter permissions
$permissionField = $form->Fields()->dataFieldByName('Permissions');
if($permissionField) $permissionField->setHiddenPermissions(self::$hidden_permissions);
}
$this->extend('updateEditForm', $form);
} else {
$form = $this->RootForm();
}
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
if($form->Fields()->hasTabset()) $form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet');
$form->addExtraClass('center ss-tabset ' . $this->BaseCSSClasses());
return $form;
}
/**
* @return FieldList
*/
function RootForm() {
$config = new GridFieldConfig_Base(25);
$config->addComponent(new GridFieldAction_Edit());
$config->addComponent(new GridFieldAction_Delete());
$config->addComponent(new GridFieldPopupForms());
$config->addComponent(new GridFieldExporter());
$memberList = new GridField('Members', 'All members', DataList::create('Member'), $config);
$memberList->addExtraClass("members_grid");
$fields = new FieldList( $fields = new FieldList(
$root = new TabSet( $root = new TabSet(
'Root', 'Root',
new Tab('Members', singleton('Member')->i18n_plural_name(), new Tab('Users', _t('SecurityAdmin.Users', 'Users'),
$memberList, $memberList,
new LiteralField('MembersCautionText', new LiteralField('MembersCautionText',
sprintf('<p class="caution-remove"><strong>%s</strong></p>', sprintf('<p class="caution-remove"><strong>%s</strong></p>',
@ -122,14 +76,8 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
) )
) )
), ),
new Tab('Import', _t('SecurityAdmin.TABIMPORT', 'Import'), new Tab('Groups', singleton('Group')->plural_name(),
new LiteralField( $groupList
'GroupImportFormIframe',
sprintf(
'<iframe src="%s" id="GroupImportFormIframe" width="100%%" height="400px" border="0"></iframe>',
$this->Link('groupimport')
)
)
) )
), ),
// necessary for tree node selection in LeftAndMain.EditForm.js // necessary for tree node selection in LeftAndMain.EditForm.js
@ -140,24 +88,34 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
// Add roles editing interface // Add roles editing interface
if(Permission::check('APPLY_ROLES')) { if(Permission::check('APPLY_ROLES')) {
$rolesCTF = new ComplexTableField( $rolesField = new GridField(
$this,
'Roles', 'Roles',
'PermissionRole' false,
DataList::create('PermissionRole'),
GridFieldConfig_RecordEditor::create()
); );
$rolesCTF->setPermissions(array('add', 'edit', 'delete')); // $rolesCTF->setPermissions(array('add', 'edit', 'delete'));
$rolesTab = $fields->findOrMakeTab('Root.Roles', _t('SecurityAdmin.TABROLES', 'Roles')); $rolesTab = $fields->findOrMakeTab('Root.Roles', _t('SecurityAdmin.TABROLES', 'Roles'));
$rolesTab->push(new LiteralField( $rolesTab->push(new LiteralField(
'RolesDescription', 'RolesDescription',
'' ''
)); ));
$rolesTab->push($rolesCTF); $rolesTab->push($rolesField);
} }
$actions = new FieldList(); $fields->findOrMakeTab('Root.Import', _t('SecurityAdmin.TABIMPORT', 'Import'));
$fields->addFieldToTab('Root.Import',
new LiteralField(
'GroupImportFormIframe',
sprintf(
'<iframe src="%s" id="GroupImportFormIframe" width="100%%" height="400px" border="0"></iframe>',
$this->Link('groupimport')
)
)
);
$this->extend('updateRootFormFields', $fields, $actions); $actions = new FieldList();
$form = new Form( $form = new Form(
$this, $this,
@ -166,13 +124,11 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
$actions $actions
); );
$form->addExtraClass('cms-edit-form'); $form->addExtraClass('cms-edit-form');
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
if($form->Fields()->hasTabset()) $form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet');
$form->addExtraClass('center ss-tabset ' . $this->BaseCSSClasses());
return $form; $this->extend('updateEditForm', $form);
}
function AddForm() {
$form = parent::AddForm();
$form->Actions()->fieldByName('action_doAdd')->setTitle(_t('SecurityAdmin.ActionAdd', 'Add group'));
return $form; return $form;
} }
@ -237,61 +193,41 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
return $form; return $form;
} }
public function AddRecordForm() { public function Breadcrumbs($unlinked = false) {
$m = Object::create('MemberTableField', $crumbs = parent::Breadcrumbs($unlinked);
$this,
"Members",
$this->currentPageID()
);
return $m->AddRecordForm();
}
/** // Name root breadcrumb based on which record is edited,
* Ajax autocompletion // which can only be determined by looking for the fieldname of the GridField.
*/ // Note: Titles should be same titles as tabs in RootForm().
public function autocomplete() { $params = $this->request->allParams();
$fieldName = $this->urlParams['ID']; if(isset($params['FieldName'])) {
$fieldVal = $_REQUEST[$fieldName]; if($params['FieldName'] == 'Groups') {
$result = ''; $crumbs->First()->Title = singleton('Group')->plural_name();
$uidField = Member::get_unique_identifier_field(); } elseif($params['FieldName'] == 'Users') {
$crumbs->First()->Title = _t('SecurityAdmin.Users', 'Users');
// Make sure we only autocomplete on keys that actually exist, and that we don't autocomplete on password } elseif($params['FieldName'] == 'Roles') {
if(!singleton($this->stat('subitem_class'))->hasDatabaseField($fieldName) || $fieldName == 'Password') return; $crumbs->First()->Title = _t('SecurityAdmin.TABROLES', 'Roles');
$matches = DataObject::get($this->stat('subitem_class'),"\"$fieldName\" LIKE '" . Convert::raw2sql($fieldVal) . "%'");
if($matches) {
$result .= "<ul>";
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(
'<li data-fields="%s">%s <span class="informal">(%s)</span></li>',
Convert::raw2att(Convert::raw2json($data)),
$match->$fieldName,
implode(',', array_values($data))
);
} }
$result .= "</ul>"; } else {
return $result; // Avoid writing "Users" (the controller menu title) as a breadcrumb
// because its confusing and inaccurate.
$crumbs = new ArrayList();
} }
}
function getCMSTreeTitle() { return $crumbs;
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() { function providePermissions() {
$title = _t("SecurityAdmin.MENUTITLE", LeftAndMain::menu_title_for_class($this->class));
return array( return array(
"CMS_ACCESS_SecurityAdmin" => array(
'name' => sprintf(_t('CMSMain.ACCESS', "Access to '%s' section"), $title),
'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access'),
'help' => _t(
'SecurityAdmin.ACCESS_HELP',
'Allow viewing, adding and editing users, as well as assigning permissions and roles to them.'
)
),
'EDIT_PERMISSIONS' => array( 'EDIT_PERMISSIONS' => array(
'name' => _t('SecurityAdmin.EDITPERMISSIONS', 'Manage permissions for groups'), 'name' => _t('SecurityAdmin.EDITPERMISSIONS', 'Manage permissions for groups'),
'category' => _t('Permissions.PERMISSIONS_CATEGORY', 'Roles and access permissions'), 'category' => _t('Permissions.PERMISSIONS_CATEGORY', 'Roles and access permissions'),
@ -301,7 +237,7 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
'APPLY_ROLES' => array( 'APPLY_ROLES' => array(
'name' => _t('SecurityAdmin.APPLY_ROLES', 'Apply roles to groups'), 'name' => _t('SecurityAdmin.APPLY_ROLES', 'Apply roles to groups'),
'category' => _t('Permissions.PERMISSIONS_CATEGORY', 'Roles and access permissions'), '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.'), 'help' => _t('SecurityAdmin.APPLY_ROLES_HELP', 'Ability to edit the roles assigned to a group. Requires the "Access to \'Users\' section" permission.'),
'sort' => 0 'sort' => 0
) )
); );
@ -340,36 +276,3 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
self::$hidden_permissions = array(); 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(SS_List $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);
}
}

View File

@ -15,4 +15,33 @@ html { overflow: hidden; }
.jstree li a .ui-icon { text-indent: 0px !important; } .jstree li a .ui-icon { text-indent: 0px !important; }
.cms table.ss-gridfield-table tbody td button span.ui-button-text { zoom: 1; } .cms table.ss-gridfield-table tr th.extra span input { height: 23px; }
.ss-gridfield-button-filter.ss-ui-button { margin: -1px -5px; background: #e6e6e6 url(../images/filter-icons.png) no-repeat -40px 6px; filter: none; }
.ss-gridfield-button-filter.ss-ui-button.hover-alike:active { background: #2e7ead url(../images/filter-icons.png) no-repeat -15px 7px; filter: none; }
.ss-gridfield-button-filter.ss-ui-button.hover-alike { background: #338dc1 url(../images/filter-icons.png) no-repeat -16px 6px; filter: none; }
.ss-gridfield-button-reset.ss-ui-button { margin: -1px -5px; background: #e6e6e6 url(../images/filter-icons.png) no-repeat 8px 5px; filter: none; }
.ss-gridfield-button-reset.ss-ui-button.filtered:hover { background: red url(../images/filter-icons.png) no-repeat 8px -17px; filter: none; }
.ss-gridfield-button-reset.ss-ui-button.filtered:active { background: #e60000 url(../images/filter-icons.png) no-repeat 9px -16px; filter: none; }
.cms table.ss-gridfield-table tr td { border-right: 1px solid #9a9a9a; }
.cms table.ss-gridfield-table tr th { border-right: 1px solid #9a9a9a; }
.cms table.ss-gridfield-table tr th.main { border-top: 1px solid #9a9a9a; border-bottom: none; }
.cms table.ss-gridfield-table tr th.extra { border-top: 1px solid #9a9a9a; padding-right: 12px; }
.cms table.ss-gridfield-table td:first-child, .cms table.ss-gridfield-table th:first-child { border-left: 1px solid #9a9a9a; }
.cms .ss-gridfield table.ss-gridfield-table tbody td button.gridfield-button-delete { display: block; float: left; }
.cms .ss-gridfield table.ss-gridfield-table tbody td button.gridfield-button-unlink.ui-state-hover { display: block; float: left; }
.cms .ss-gridfield table.ss-gridfield-table tbody td a.edit-link { display: block; float: left; }
.cms .cms-content .cms-content-fields .aligned_right_label { margin-left: 0; }
.cms .cms-content .cms-content-fields .field.dropdown .middleColumn { max-width: 512px; }
.pagination-page-number { position: relative; bottom: 10px; right: 10px; }
.pagination-page-number input { width: 45px; padding: 0px; position: relative; bottom: 2px; }
table.ss-gridfield-table tr.title th h2 { float: left; }
table.ss-gridfield-table tr.ss-gridfield-item.odd { background: white; }
table.ss-gridfield-table tr.ss-gridfield-item.even { background: #F0F4F7; }

View File

@ -8,3 +8,19 @@
.ss-ui-button.cms-page-add-button.ui-state-hover, .cms .ss-ui-button.cms-page-add-button:hover { background: #80bf40 url("../images/btn-icon-saaa1989272.png") 5px -49px no-repeat; } .ss-ui-button.cms-page-add-button.ui-state-hover, .cms .ss-ui-button.cms-page-add-button:hover { background: #80bf40 url("../images/btn-icon-saaa1989272.png") 5px -49px no-repeat; }
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { background-image: none; } .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { background-image: none; }
.ss-gridfield-button-filter.ss-ui-button { background: #e6e6e6 url(../images/filter-icons.png) no-repeat -40px 6px; filter: none; }
.ss-gridfield-button-filter.ss-ui-button.hover-alike:active { background: #2e7ead url(../images/filter-icons.png) no-repeat -15px 7px; filter: none; }
.ss-gridfield-button-filter.ss-ui-button.hover-alike { background: #338dc1 url(../images/filter-icons.png) no-repeat -16px 6px; filter: none; }
.ss-gridfield-button-reset.ss-ui-button { background: #e6e6e6 url(../images/filter-icons.png) no-repeat 8px 5px; filter: none; }
.ss-gridfield-button-reset.ss-ui-button.filtered:hover { background: red url(../images/filter-icons.png) no-repeat 8px -17px; filter: none; }
.ss-gridfield-button-reset.ss-ui-button.filtered:active { background: #e60000 url(../images/filter-icons.png) no-repeat 9px -16px; filter: none; }
.cms table.ss-gridfield-table tr td { border-right: 1px solid #9a9a9a; }
.cms table.ss-gridfield-table tr th { border-right: 1px solid #9a9a9a; }
.cms table.ss-gridfield-table tr th.main { border-top: 1px solid #9a9a9a; border-bottom: none; }
.cms table.ss-gridfield-table tr th.extra { border-top: 1px solid #9a9a9a; }
.cms table.ss-gridfield-table td:first-child, .cms table.ss-gridfield-table th:first-child { border-left: 1px solid #9a9a9a; }
.cms .cms-content .cms-content-fields .aligned_right_label { margin-left: 0; }

View File

@ -31,7 +31,7 @@ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav,
/** ---------------------------------------------------- Double tone borders http://daverupert.com/2011/06/two-tone-borders-with-css3/ ----------------------------------------------------- */ /** ---------------------------------------------------- Double tone borders http://daverupert.com/2011/06/two-tone-borders-with-css3/ ----------------------------------------------------- */
/** ----------------------------- Sprite images ----------------------------- */ /** ----------------------------- Sprite images ----------------------------- */
/** Helper SCSS file for generating sprites for the interface. */ /** Helper SCSS file for generating sprites for the interface. */
.btn-icon-sprite, .ui-state-default .btn-icon-accept, .ui-state-default .btn-icon-accept_disabled, .ui-state-default .btn-icon-add, .ui-state-default .btn-icon-add_disabled, .ui-state-default .btn-icon-addpage, .ui-state-default .btn-icon-addpage_disabled, .ui-state-default .btn-icon-arrow-circle-135-left, .ui-state-default .btn-icon-back, .ui-state-default .btn-icon-back_disabled, .ui-state-default .btn-icon-cross-circle, .ui-state-default .btn-icon-cross-circle_disabled, .ui-state-default .btn-icon-decline, .ui-state-default .btn-icon-decline_disabled, .ui-state-default .btn-icon-drive-upload, .ui-state-default .btn-icon-drive-upload_disabled, .ui-state-default .btn-icon-magnifier, .ui-state-default .btn-icon-minus-circle, .ui-state-default .btn-icon-minus-circle_disabled, .ui-state-default .btn-icon-navigation, .ui-state-default .btn-icon-navigation_disabled, .ui-state-default .btn-icon-network-cloud, .ui-state-default .btn-icon-network-cloud_disabled, .ui-state-default .btn-icon-pencil, .ui-state-default .btn-icon-pencil_disabled, .ui-state-default .btn-icon-plug-disconnect-prohibition, .ui-state-default .btn-icon-plug-disconnect-prohibition_disabled, .ui-state-default .btn-icon-preview, .ui-state-default .btn-icon-preview_disabled, .ui-state-default .btn-icon-settings, .ui-state-default .btn-icon-settings_disabled, .ui-state-default .btn-icon-unpublish, .ui-state-default .btn-icon-unpublish_disabled { background: url('../images/btn-icon-s4a0551bc3f.png') no-repeat; } .btn-icon-sprite, .ui-state-default .btn-icon-accept, .ui-state-default .btn-icon-accept_disabled, .ui-state-default .btn-icon-add, .ui-state-default .btn-icon-add_disabled, .ui-state-default .btn-icon-addpage, .ui-state-default .btn-icon-addpage_disabled, .ui-state-default .btn-icon-arrow-circle-135-left, .ui-state-default .btn-icon-back, .ui-state-default .btn-icon-back_disabled, .ui-state-default .btn-icon-chain--arrow, .ui-state-default .btn-icon-chain--exclamation, .ui-state-default .btn-icon-chain--minus, .ui-state-default .btn-icon-chain--pencil, .ui-state-default .btn-icon-chain--plus, .ui-state-default .btn-icon-chain-small, .ui-state-default .btn-icon-chain-unchain, .ui-state-default .btn-icon-chain, .ui-state-default .btn-icon-cross-circle, .ui-state-default .btn-icon-cross-circle_disabled, .ui-state-default .btn-icon-decline, .ui-state-default .btn-icon-decline_disabled, .ui-state-default .btn-icon-download-csv, .ui-state-default .btn-icon-drive-upload, .ui-state-default .btn-icon-drive-upload_disabled, .ui-state-default .btn-icon-magnifier, .ui-state-default .btn-icon-minus-circle, .ui-state-default .btn-icon-minus-circle_disabled, .ui-state-default .btn-icon-navigation, .ui-state-default .btn-icon-navigation_disabled, .ui-state-default .btn-icon-network-cloud, .ui-state-default .btn-icon-network-cloud_disabled, .ui-state-default .btn-icon-pencil, .ui-state-default .btn-icon-pencil_disabled, .ui-state-default .btn-icon-plug-disconnect-prohibition, .ui-state-default .btn-icon-plug-disconnect-prohibition_disabled, .ui-state-default .btn-icon-preview, .ui-state-default .btn-icon-preview_disabled, .ui-state-default .btn-icon-settings, .ui-state-default .btn-icon-settings_disabled, .ui-state-default .btn-icon-unpublish, .ui-state-default .btn-icon-unpublish_disabled { background: url('../images/btn-icon-sb5d07676ff.png') no-repeat; }
.ui-state-default .btn-icon-accept { background-position: 0 0; } .ui-state-default .btn-icon-accept { background-position: 0 0; }
.ui-state-default .btn-icon-accept_disabled { background-position: 0 -17px; } .ui-state-default .btn-icon-accept_disabled { background-position: 0 -17px; }
@ -41,30 +41,39 @@ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav,
.ui-state-default .btn-icon-addpage_disabled { background-position: 0 -88px; } .ui-state-default .btn-icon-addpage_disabled { background-position: 0 -88px; }
.ui-state-default .btn-icon-arrow-circle-135-left { background-position: 0 -104px; } .ui-state-default .btn-icon-arrow-circle-135-left { background-position: 0 -104px; }
.ui-state-default .btn-icon-back { background-position: 0 -120px; } .ui-state-default .btn-icon-back { background-position: 0 -120px; }
.ui-state-default .btn-icon-back_disabled { background-position: 0 -136px; } .ui-state-default .btn-icon-back_disabled { background-position: 0 -135px; }
.ui-state-default .btn-icon-cross-circle { background-position: 0 -152px; } .ui-state-default .btn-icon-chain--arrow { background-position: 0 -150px; }
.ui-state-default .btn-icon-cross-circle_disabled { background-position: 0 -168px; } .ui-state-default .btn-icon-chain--exclamation { background-position: 0 -166px; }
.ui-state-default .btn-icon-decline { background-position: 0 -184px; } .ui-state-default .btn-icon-chain--minus { background-position: 0 -182px; }
.ui-state-default .btn-icon-decline_disabled { background-position: 0 -201px; } .ui-state-default .btn-icon-chain--pencil { background-position: 0 -198px; }
.ui-state-default .btn-icon-drive-upload { background-position: 0 -218px; } .ui-state-default .btn-icon-chain--plus { background-position: 0 -214px; }
.ui-state-default .btn-icon-drive-upload_disabled { background-position: 0 -234px; } .ui-state-default .btn-icon-chain-small { background-position: 0 -230px; }
.ui-state-default .btn-icon-magnifier { background-position: 0 -250px; } .ui-state-default .btn-icon-chain-unchain { background-position: 0 -246px; }
.ui-state-default .btn-icon-minus-circle { background-position: 0 -266px; } .ui-state-default .btn-icon-chain { background-position: 0 -262px; }
.ui-state-default .btn-icon-minus-circle_disabled { background-position: 0 -282px; } .ui-state-default .btn-icon-cross-circle { background-position: 0 -278px; }
.ui-state-default .btn-icon-navigation { background-position: 0 -298px; } .ui-state-default .btn-icon-cross-circle_disabled { background-position: 0 -294px; }
.ui-state-default .btn-icon-navigation_disabled { background-position: 0 -314px; } .ui-state-default .btn-icon-decline { background-position: 0 -310px; }
.ui-state-default .btn-icon-network-cloud { background-position: 0 -330px; } .ui-state-default .btn-icon-decline_disabled { background-position: 0 -327px; }
.ui-state-default .btn-icon-network-cloud_disabled { background-position: 0 -346px; } .ui-state-default .btn-icon-download-csv { background-position: 0 -344px; }
.ui-state-default .btn-icon-pencil { background-position: 0 -362px; } .ui-state-default .btn-icon-drive-upload { background-position: 0 -362px; }
.ui-state-default .btn-icon-pencil_disabled { background-position: 0 -378px; } .ui-state-default .btn-icon-drive-upload_disabled { background-position: 0 -378px; }
.ui-state-default .btn-icon-plug-disconnect-prohibition { background-position: 0 -394px; } .ui-state-default .btn-icon-magnifier { background-position: 0 -394px; }
.ui-state-default .btn-icon-plug-disconnect-prohibition_disabled { background-position: 0 -410px; } .ui-state-default .btn-icon-minus-circle { background-position: 0 -410px; }
.ui-state-default .btn-icon-preview { background-position: 0 -426px; } .ui-state-default .btn-icon-minus-circle_disabled { background-position: 0 -426px; }
.ui-state-default .btn-icon-preview_disabled { background-position: 0 -443px; } .ui-state-default .btn-icon-navigation { background-position: 0 -442px; }
.ui-state-default .btn-icon-settings { background-position: 0 -460px; } .ui-state-default .btn-icon-navigation_disabled { background-position: 0 -458px; }
.ui-state-default .btn-icon-settings_disabled { background-position: 0 -476px; } .ui-state-default .btn-icon-network-cloud { background-position: 0 -474px; }
.ui-state-default .btn-icon-unpublish { background-position: 0 -492px; } .ui-state-default .btn-icon-network-cloud_disabled { background-position: 0 -490px; }
.ui-state-default .btn-icon-unpublish_disabled { background-position: 0 -510px; } .ui-state-default .btn-icon-pencil { background-position: 0 -506px; }
.ui-state-default .btn-icon-pencil_disabled { background-position: 0 -522px; }
.ui-state-default .btn-icon-plug-disconnect-prohibition { background-position: 0 -538px; }
.ui-state-default .btn-icon-plug-disconnect-prohibition_disabled { background-position: 0 -554px; }
.ui-state-default .btn-icon-preview { background-position: 0 -570px; }
.ui-state-default .btn-icon-preview_disabled { background-position: 0 -587px; }
.ui-state-default .btn-icon-settings { background-position: 0 -604px; }
.ui-state-default .btn-icon-settings_disabled { background-position: 0 -620px; }
.ui-state-default .btn-icon-unpublish { background-position: 0 -636px; }
.ui-state-default .btn-icon-unpublish_disabled { background-position: 0 -654px; }
.icon { text-indent: -9999px; border: none; outline: none; } .icon { text-indent: -9999px; border: none; outline: none; }
.icon.icon-24 { width: 24px; height: 24px; background: url('../images/menu-icons/24x24-s546fcae8fd.png'); } .icon.icon-24 { width: 24px; height: 24px; background: url('../images/menu-icons/24x24-s546fcae8fd.png'); }
@ -220,7 +229,7 @@ html, body { width: 100%; height: 100%; }
body.cms { overflow: hidden; } body.cms { overflow: hidden; }
.cms a { color: #3ebae0; text-decoration: none; } .cms a { color: #1556b2; text-decoration: none; }
.cms a:hover, .cms a:focus { text-decoration: underline; } .cms a:hover, .cms a:focus { text-decoration: underline; }
.cms body .ui-widget { font-family: Arial, sans-serif; font-size: 12px; } .cms body .ui-widget { font-family: Arial, sans-serif; font-size: 12px; }
.cms strong { font-weight: bold; } .cms strong { font-weight: bold; }
@ -237,18 +246,13 @@ body.cms { overflow: hidden; }
.cms-content-header { padding: 0px 8px 8px; height: 32px; z-index: 60; background-image: url(../images/textures/cms_content_header.png); background-repeat: repeat; } .cms-content-header { padding: 0px 8px 8px; height: 32px; z-index: 60; background-image: url(../images/textures/cms_content_header.png); background-repeat: repeat; }
.cms-content-header a { color: #1556b2; } .cms-content-header a { color: #1556b2; }
.cms-content-header .backlink { float: left; margin-top: 7px; } .cms-content-header .backlink { float: left; margin-top: 7px; }
.cms-content-header .backlink span.btn-icon-back { height: 15px; }
.cms-content-header h2 { float: left; padding: 8px 8px 0 8px; font-size: 14px; line-height: 24px; font-weight: bold; text-shadow: #bfcad2 1px 1px 0; max-width: 400px; margin: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; o-text-overflow: ellipsis; } .cms-content-header h2 { float: left; padding: 8px 8px 0 8px; font-size: 14px; line-height: 24px; font-weight: bold; text-shadow: #bfcad2 1px 1px 0; max-width: 400px; margin: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; o-text-overflow: ellipsis; }
.cms-content-header > div { width: 9999em; overflow: hidden; } .cms-content-header > div { width: 9999em; overflow: hidden; }
.cms-content-header .cms-content-header-tabs { position: fixed; right: 40px; } .cms-content-header .cms-content-header-tabs { position: fixed; right: 40px; }
.cms-content-header .cms-content-header-tabs .ui-tabs-nav li a { font-weight: bold; line-height: 16px; padding: 12px 20px 11px; text-indent: -9999em; } .cms-content-header .cms-content-header-tabs .ui-tabs-nav li a { font-weight: bold; line-height: 16px; padding: 12px 20px 11px; }
.cms-content-header .cms-content-header-tabs .ui-tabs-nav li a.content-treeview { background: url(../images/content-header-tabs-sprite.png) no-repeat 2px 0px; }
.cms-content-header .cms-content-header-tabs .ui-tabs-nav li a.content-galleryview { background: url(../images/content-header-tabs-sprite.png) no-repeat -87px 0px; }
.cms-content-header .cms-content-header-tabs .ui-tabs-nav li a.content-listview { background: url(../images/content-header-tabs-sprite.png) no-repeat -38px 0px; }
.cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-state-default, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-widget-content .ui-state-default, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-widget-header .ui-state-default { border-top: none; } .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-state-default, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-widget-content .ui-state-default, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-widget-header .ui-state-default { border-top: none; }
.cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-state-active, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-widget-content .ui-state-active, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-widget-header .ui-state-active { border-top: none; } .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-state-active, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-widget-content .ui-state-active, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-widget-header .ui-state-active { border-top: none; }
.cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-state-active a.content-treeview, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-widget-content .ui-state-active a.content-treeview, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-widget-header .ui-state-active a.content-treeview { background: url(../images/content-header-tabs-sprite.png) no-repeat 2px -40px; }
.cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-state-active a.content-galleryview, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-widget-content .ui-state-active a.content-galleryview, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-widget-header .ui-state-active a.content-galleryview { background: url(../images/content-header-tabs-sprite.png) no-repeat -87px -40px; }
.cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-state-active a.content-listview, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-widget-content .ui-state-active a.content-listview, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-widget-header .ui-state-active a.content-listview { background: url(../images/content-header-tabs-sprite.png) no-repeat -38px -40px; }
.cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-corner-all, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-corner-top, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-corner-right, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-corner-tr { border-radius: 0; } .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-corner-all, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-corner-top, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-corner-right, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-corner-tr { border-radius: 0; }
.cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-corner-all, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-corner-top, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-corner-left, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-corner-tl { border-radius: 0; } .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-corner-all, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-corner-top, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-corner-left, .cms-content-header .cms-content-header-tabs .ui-tabs-nav .ui-corner-tl { border-radius: 0; }
@ -262,6 +266,15 @@ body.cms { overflow: hidden; }
.ui-tabs .cms-content-header .ui-tabs-nav .ui-state-active, .ui-tabs .cms-content-header .ui-tabs-nav .ui-widget-content .ui-state-active, .ui-tabs .cms-content-header .ui-tabs-nav .ui-widget-header .ui-state-active, .cms-dialog .ui-tabs-nav .ui-state-active, .cms-dialog .ui-tabs-nav .ui-widget-content .ui-state-active, .cms-dialog .ui-tabs-nav .ui-widget-header .ui-state-active { background: #eceff1; border-right-color: #a6a6a6; border-left-color: #a6a6a6; margin-right: -1px; margin-left: -1px; z-index: 2; } .ui-tabs .cms-content-header .ui-tabs-nav .ui-state-active, .ui-tabs .cms-content-header .ui-tabs-nav .ui-widget-content .ui-state-active, .ui-tabs .cms-content-header .ui-tabs-nav .ui-widget-header .ui-state-active, .cms-dialog .ui-tabs-nav .ui-state-active, .cms-dialog .ui-tabs-nav .ui-widget-content .ui-state-active, .cms-dialog .ui-tabs-nav .ui-widget-header .ui-state-active { background: #eceff1; border-right-color: #a6a6a6; border-left-color: #a6a6a6; margin-right: -1px; margin-left: -1px; z-index: 2; }
.ui-tabs .cms-content-header .ui-tabs-nav .ui-state-active a, .ui-tabs .cms-content-header .ui-tabs-nav .ui-widget-content .ui-state-active a, .ui-tabs .cms-content-header .ui-tabs-nav .ui-widget-header .ui-state-active a, .cms-dialog .ui-tabs-nav .ui-state-active a, .cms-dialog .ui-tabs-nav .ui-widget-content .ui-state-active a, .cms-dialog .ui-tabs-nav .ui-widget-header .ui-state-active a { border-bottom: none; } .ui-tabs .cms-content-header .ui-tabs-nav .ui-state-active a, .ui-tabs .cms-content-header .ui-tabs-nav .ui-widget-content .ui-state-active a, .ui-tabs .cms-content-header .ui-tabs-nav .ui-widget-header .ui-state-active a, .cms-dialog .ui-tabs-nav .ui-state-active a, .cms-dialog .ui-tabs-nav .ui-widget-content .ui-state-active a, .cms-dialog .ui-tabs-nav .ui-widget-header .ui-state-active a { border-bottom: none; }
.CMSPagesController .cms-content-header-tabs .ui-tabs-nav li a { font-weight: bold; line-height: 16px; padding: 12px 20px 11px; text-indent: -9999em; }
.CMSPagesController .cms-content-header-tabs .ui-tabs-nav li a.content-treeview { background: url(../images/content-header-tabs-sprite.png) no-repeat 2px 0px; }
.CMSPagesController .cms-content-header-tabs .ui-tabs-nav li a.content-galleryview { background: url(../images/content-header-tabs-sprite.png) no-repeat -87px 0px; }
.CMSPagesController .cms-content-header-tabs .ui-tabs-nav li a.content-listview { background: url(../images/content-header-tabs-sprite.png) no-repeat -38px 0px; }
.CMSPagesController .cms-content-header-tabs .ui-tabs-nav .ui-state-active, .CMSPagesController .cms-content-header-tabs .ui-tabs-nav .ui-widget-content .ui-state-active, .CMSPagesController .cms-content-header-tabs .ui-tabs-nav .ui-widget-header .ui-state-active { border-top: none; }
.CMSPagesController .cms-content-header-tabs .ui-tabs-nav .ui-state-active a.content-treeview, .CMSPagesController .cms-content-header-tabs .ui-tabs-nav .ui-widget-content .ui-state-active a.content-treeview, .CMSPagesController .cms-content-header-tabs .ui-tabs-nav .ui-widget-header .ui-state-active a.content-treeview { background: url(../images/content-header-tabs-sprite.png) no-repeat 2px -40px; }
.CMSPagesController .cms-content-header-tabs .ui-tabs-nav .ui-state-active a.content-galleryview, .CMSPagesController .cms-content-header-tabs .ui-tabs-nav .ui-widget-content .ui-state-active a.content-galleryview, .CMSPagesController .cms-content-header-tabs .ui-tabs-nav .ui-widget-header .ui-state-active a.content-galleryview { background: url(../images/content-header-tabs-sprite.png) no-repeat -87px -40px; }
.CMSPagesController .cms-content-header-tabs .ui-tabs-nav .ui-state-active a.content-listview, .CMSPagesController .cms-content-header-tabs .ui-tabs-nav .ui-widget-content .ui-state-active a.content-listview, .CMSPagesController .cms-content-header-tabs .ui-tabs-nav .ui-widget-header .ui-state-active a.content-listview { background: url(../images/content-header-tabs-sprite.png) no-repeat -38px -40px; }
/** ------------------------------------------------------- Loading Interface ------------------------------------------------------- */ /** ------------------------------------------------------- Loading Interface ------------------------------------------------------- */
.cms-content-loading-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 9998; } .cms-content-loading-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 9998; }
@ -282,7 +295,7 @@ body.cms { overflow: hidden; }
.message.notice { background-color: #ffbe66; border-color: #ff9300; } .message.notice { background-color: #ffbe66; border-color: #ff9300; }
.message.notice a { color: #999; } .message.notice a { color: #999; }
.message.warning { background-color: #ffbe66; border-color: #ff9300; } .message.warning { background-color: #ffbe66; border-color: #ff9300; }
.message.error { background-color: #ffbe66; border-color: #ff9300; } .message.error, .message.bad, .message.required { background-color: #ffbe66; border-color: #ff9300; }
.message.good { background-color: #99cc66; border-color: #669933; } .message.good { background-color: #99cc66; border-color: #669933; }
.message p { margin: 0; } .message p { margin: 0; }
@ -347,7 +360,6 @@ body.cms { overflow: hidden; }
.cms-content-batchactions { float: left; position: relative; display: block; margin-left: 8px; } .cms-content-batchactions { float: left; position: relative; display: block; margin-left: 8px; }
.cms-content-batchactions form > * { display: block; float: left; } .cms-content-batchactions form > * { display: block; float: left; }
.cms-content-batchactions form.cms-batch-actions { float: left; } .cms-content-batchactions form.cms-batch-actions { float: left; }
.cms-content-batchactions .Actions { display: none; }
#Form_BatchActionsForm select { width: 200px; } #Form_BatchActionsForm select { width: 200px; }
@ -388,6 +400,12 @@ form.member-profile-form .ui-corner-all, form.member-profile-form .ui-corner-top
.cms .cms-content { border-right: 1px solid rgba(201, 205, 206, 0.8); -moz-box-shadow: 3px 0 4px rgba(0, 0, 0, 0.15); -webkit-box-shadow: 3px 0 4px rgba(0, 0, 0, 0.15); -o-box-shadow: 3px 0 4px rgba(0, 0, 0, 0.15); box-shadow: 3px 0 4px rgba(0, 0, 0, 0.15); -moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; -ms-border-radius: 0; -khtml-border-radius: 0; border-radius: 0; } .cms .cms-content { border-right: 1px solid rgba(201, 205, 206, 0.8); -moz-box-shadow: 3px 0 4px rgba(0, 0, 0, 0.15); -webkit-box-shadow: 3px 0 4px rgba(0, 0, 0, 0.15); -o-box-shadow: 3px 0 4px rgba(0, 0, 0, 0.15); box-shadow: 3px 0 4px rgba(0, 0, 0, 0.15); -moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; -ms-border-radius: 0; -khtml-border-radius: 0; border-radius: 0; }
.cms .cms-content-fields { /* always show a y scroll bar as popups like TreeDropdowns can trigger longer pages and the extra scroll bar doesn't fire our sizing bar */ overflow-y: auto; overflow-x: auto; background: none; width: 100%; } .cms .cms-content-fields { /* always show a y scroll bar as popups like TreeDropdowns can trigger longer pages and the extra scroll bar doesn't fire our sizing bar */ overflow-y: auto; overflow-x: auto; background: none; width: 100%; }
.cms .cms-content-fields .aligned_right_label { margin-left: 184px; padding: 8px 0; }
/** -------------------------------------------- "Settings" Form -------------------------------------------- */
#CanViewType .optionset li, #CanEditType .optionset li, #CanCreateTopLevelType .optionset li { float: none; width: auto; }
#ViewerGroups select, #EditorGroups select, #CreateTopLevelGroups select { width: 512px; }
/** -------------------------------------------- Panels -------------------------------------------- */ /** -------------------------------------------- Panels -------------------------------------------- */
.cms-panel { overflow: hidden; } .cms-panel { overflow: hidden; }
@ -409,7 +427,7 @@ form.member-profile-form .ui-corner-all, form.member-profile-form .ui-corner-top
.cms-panel .collapsed-flyout { display: block !important; left: 41px; margin-top: -40px; position: fixed; width: 191px; } .cms-panel .collapsed-flyout { display: block !important; left: 41px; margin-top: -40px; position: fixed; width: 191px; }
.cms-panel .collapsed-flyout li a span { display: block !important; } .cms-panel .collapsed-flyout li a span { display: block !important; }
.cms-panel-padded { width: 176px; padding: 16px 16px; overflow-x: hidden; overflow-y: auto; } .cms .cms-panel-padded { width: 176px; padding: 16px 16px; overflow-x: hidden; overflow-y: auto; }
/** ------------------------------------------------------------------ /** ------------------------------------------------------------------
* Dialog * Dialog
@ -473,6 +491,12 @@ form.small .cms-file-info-data .field { padding-bottom: 0; }
form.small .cms-file-info-data .field label { width: 112px; } form.small .cms-file-info-data .field label { width: 112px; }
form.small .cms-file-info-data .field .middleColumn { margin-left: 120px; } form.small .cms-file-info-data .field .middleColumn { margin-left: 120px; }
/** -------------------------------------------- Users Members Admin -------------------------------------------- */
.members_grid span button#action_gridfield_relationfind { display: none; }
.members_grid p button#action_export { margin-top: 16px; }
.members_grid p button#action_export span.btn-icon-download-csv { height: 17px; }
.members_grid p button#action_export span.ui-button-text { padding-left: 27px; }
/** This file defines the jstree base styling (see http://jstree.com), as well as any customizations (see bottom of file). The styles are usually added through jstree.js on DOM load, but we need it earlier in order to correctly display the uninitialized tree. */ /** This file defines the jstree base styling (see http://jstree.com), as well as any customizations (see bottom of file). The styles are usually added through jstree.js on DOM load, but we need it earlier in order to correctly display the uninitialized tree. */
.cms .jstree ul, .TreeDropdownField .treedropdownfield-panel .jstree ul { display: block; margin: 0; padding: 0; background: none; list-style-type: none; } .cms .jstree ul, .TreeDropdownField .treedropdownfield-panel .jstree ul { display: block; margin: 0; padding: 0; background: none; list-style-type: none; }
.cms .jstree li, .TreeDropdownField .treedropdownfield-panel .jstree li { display: block; margin: 0; padding: 0; list-style-type: none; display: block; min-height: 18px; line-height: 18px; white-space: nowrap; margin-left: 18px; min-width: 18px; } .cms .jstree li, .TreeDropdownField .treedropdownfield-panel .jstree li { display: block; margin: 0; padding: 0; list-style-type: none; display: block; min-height: 18px; line-height: 18px; white-space: nowrap; margin-left: 18px; min-width: 18px; }
@ -536,6 +560,8 @@ form.small .cms-file-info-data .field .middleColumn { margin-left: 120px; }
.cms .jstree-apple.jstree-focused, .TreeDropdownField .treedropdownfield-panel .jstree-apple.jstree-focused { background: none; } .cms .jstree-apple.jstree-focused, .TreeDropdownField .treedropdownfield-panel .jstree-apple.jstree-focused { background: none; }
.cms .jstree-apple.jstree-focused .jstree-apple > ul, .TreeDropdownField .treedropdownfield-panel .jstree-apple.jstree-focused .jstree-apple > ul { background: none; } .cms .jstree-apple.jstree-focused .jstree-apple > ul, .TreeDropdownField .treedropdownfield-panel .jstree-apple.jstree-focused .jstree-apple > ul { background: none; }
.cms .jstree li, .TreeDropdownField .treedropdownfield-panel .jstree li { line-height: 25px; } .cms .jstree li, .TreeDropdownField .treedropdownfield-panel .jstree li { line-height: 25px; }
.cms a > .jstree-icon, .TreeDropdownField .treedropdownfield-panel a > .jstree-icon { display: none; }
.cms .draggable a > .jstree-icon, .TreeDropdownField .treedropdownfield-panel .draggable a > .jstree-icon { display: block; }
.jstree-apple li, .jstree-apple .jstree-apple ins { background: none; } .jstree-apple li, .jstree-apple .jstree-apple ins { background: none; }
.jstree-apple .jstree-unchecked > a > .jstree-checkbox { margin-right: 3px; } .jstree-apple .jstree-unchecked > a > .jstree-checkbox { margin-right: 3px; }
@ -558,12 +584,10 @@ form.small .cms-file-info-data .field .middleColumn { margin-left: 120px; }
.tree-holder.jstree-apple .jstree-hovered, .cms-tree.jstree-apple .jstree-hovered { text-shadow: none; text-decoration: none; } .tree-holder.jstree-apple .jstree-hovered, .cms-tree.jstree-apple .jstree-hovered { text-shadow: none; text-decoration: none; }
.tree-holder.jstree-apple li, .cms-tree.jstree-apple li { padding: 0px; clear: left; } .tree-holder.jstree-apple li, .cms-tree.jstree-apple li { padding: 0px; clear: left; }
.tree-holder.jstree-apple ins, .cms-tree.jstree-apple ins { background-color: transparent; background-image: url(../images/sitetree_ss_default_icons.png); } .tree-holder.jstree-apple ins, .cms-tree.jstree-apple ins { background-color: transparent; background-image: url(../images/sitetree_ss_default_icons.png); }
.tree-holder.jstree-apple li.jstree-checked a, .tree-holder.jstree-apple li.jstree-checked a:link, .cms-tree.jstree-apple li.jstree-checked a, .cms-tree.jstree-apple li.jstree-checked a:link { background-color: #efe999; } .tree-holder.jstree-apple li.jstree-checked > a, .tree-holder.jstree-apple li.jstree-checked > a:link, .cms-tree.jstree-apple li.jstree-checked > a, .cms-tree.jstree-apple li.jstree-checked > a:link { background-color: #efe999; }
.tree-holder.jstree-apple .jstree-closed > ins, .cms-tree.jstree-apple .jstree-closed > ins { background-position: 0 0; } .tree-holder.jstree-apple .jstree-closed > ins, .cms-tree.jstree-apple .jstree-closed > ins { background-position: 0 0; }
.tree-holder.jstree-apple .jstree-open > ins, .cms-tree.jstree-apple .jstree-open > ins { background-position: -20px 0; } .tree-holder.jstree-apple .jstree-open > ins, .cms-tree.jstree-apple .jstree-open > ins { background-position: -20px 0; }
.jstree-apple #record-0.jstree-open > ins { display: none; }
a .jstree-pageicon { display: block; float: left; width: 16px; height: 16px; margin-right: 4px; background-color: transparent; background-image: url(../images/sitetree_ss_pageclass_icons_default.png); background-repeat: no-repeat; } a .jstree-pageicon { display: block; float: left; width: 16px; height: 16px; margin-right: 4px; background-color: transparent; background-image: url(../images/sitetree_ss_pageclass_icons_default.png); background-repeat: no-repeat; }
li.class-HomePage > a .jstree-pageicon { background-position: 0 -48px; } li.class-HomePage > a .jstree-pageicon { background-position: 0 -48px; }
@ -575,6 +599,8 @@ li.class-VirtualPage > a .jstree-pageicon { background-position: 0 -32px; }
li.class-ErrorPage > a .jstree-pageicon { background-position: 0 -112px; } li.class-ErrorPage > a .jstree-pageicon { background-position: 0 -112px; }
.cms-tree { visibility: hidden; } .cms-tree { visibility: hidden; }
.cms-tree.multiple li > a > .jstree-icon { display: none; }
.cms-tree.multiple li#record-0 > a .jstree-checkbox { display: none; }
/** Styles for the left hand side menu and header for the admin panels. Take into consideration CSS selector performance. @package sapphire @subpackage admin */ /** Styles for the left hand side menu and header for the admin panels. Take into consideration CSS selector performance. @package sapphire @subpackage admin */
.cms-logo-header { background-color: #00111d; position: relative; padding: 16px 8px 0 4px; line-height: 24px; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #00111d), color-stop(50%, #003050), color-stop(100%, #00111d)); background-image: -webkit-linear-gradient(#00111d, #003050, #00111d); background-image: -moz-linear-gradient(#00111d, #003050, #00111d); background-image: -o-linear-gradient(#00111d, #003050, #00111d); background-image: -ms-linear-gradient(#00111d, #003050, #00111d); background-image: linear-gradient(#00111d, #003050, #00111d); } .cms-logo-header { background-color: #00111d; position: relative; padding: 16px 8px 0 4px; line-height: 24px; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #00111d), color-stop(50%, #003050), color-stop(100%, #00111d)); background-image: -webkit-linear-gradient(#00111d, #003050, #00111d); background-image: -moz-linear-gradient(#00111d, #003050, #00111d); background-image: -o-linear-gradient(#00111d, #003050, #00111d); background-image: -ms-linear-gradient(#00111d, #003050, #00111d); background-image: linear-gradient(#00111d, #003050, #00111d); }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 742 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 B

BIN
admin/images/btn-icon/chain.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -45,12 +45,17 @@
}); });
$('.cms-tree-view-modes :input[name=view-mode]').bind('click', function(e) { $('.cms-tree-view-modes :input[name=view-mode]').bind('click', function(e) {
if($(e.target).val() == 'multiselect') { var val = $(e.target).val(), dropdown = self.find(':input[name=Action]');
if(val == 'multiselect') {
tree.addClass('multiple'); tree.addClass('multiple');
self.serializeFromTree(); self.serializeFromTree();
} else { } else {
tree.removeClass('multiple'); tree.removeClass('multiple');
} }
// Batch actions only make sense when multiselect is enabled
if(val == 'multiselect') dropdown.removeAttr('disabled').change();
else dropdown.attr('disabled', 'disabled').change();
}); });
this._super(); this._super();
@ -173,7 +178,7 @@
* {Array} ids * {Array} ids
*/ */
setIDs: function(ids) { setIDs: function(ids) {
if(ids) this.find(':input[name=csvIDs]').val(ids.join(',')); this.find(':input[name=csvIDs]').val(ids ? ids.join(',') : null);
}, },
/** /**
@ -193,8 +198,7 @@
* (Event) e * (Event) e
*/ */
onsubmit: function(e) { onsubmit: function(e) {
var ids = this.getIDs(); var self = this, ids = this.getIDs(), tree = this.getTree();
var tree = this.getTree();
// if no nodes are selected, return with an error // if no nodes are selected, return with an error
if(!ids || !ids.length) { if(!ids || !ids.length) {
@ -223,55 +227,41 @@
complete: function(xmlhttp, status) { complete: function(xmlhttp, status) {
button.removeClass('loading'); button.removeClass('loading');
// Deselect all nodes
tree.jstree('uncheck_all');
self.setIDs([]);
// Reset action
self.find(':input[name=Action]').val('').change();
// status message // status message
var msg = (xmlhttp.getResponseHeader('X-Status')) ? xmlhttp.getResponseHeader('X-Status') : xmlhttp.statusText; var msg = xmlhttp.getResponseHeader('X-Status');
statusMessage(msg, (status == 'success') ? 'good' : 'bad'); if(msg) statusMessage(msg, (status == 'success') ? 'good' : 'bad');
}, },
success: function(data, status) { success: function(data, status) {
var id; var id, node;
// TODO This should use a more common serialization in a new tree library
if(data.modified) { if(data.modified) {
var modifiedNodes = [];
for(id in data.modified) { for(id in data.modified) {
tree.jstree('set_title', tree.getNodeByID(id), data.modified[id]['TreeTitle']); node = tree.getNodeByID(id);
tree.jstree('set_text', node, data.modified[id]['TreeTitle']);
modifiedNodes.push(node);
} }
$(modifiedNodes).effect('highlight');
} }
if(data.deleted) { if(data.deleted) {
for(id in data.deleted) { for(id in data.deleted) {
var node = tree.getNodeByID(id); node = tree.getNodeByID(id);
// TODO Remove node if(node.length) tree.jstree('delete_node', node);
// if(node && node.parentTreeNode) node.parentTreeNode.removeTreeNode(node);
} }
} }
if(data.error) { if(data.error) {
for(id in data.error) { for(id in data.error) {
var node = tree.getNodeByID(id); node = tree.getNodeByID(id);
$(node).addClass('failed'); $(node).addClass('failed');
} }
} }
// Deselect all nodes
tree.find('li').removeClass('selected');
// 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('.cms-edit-form').entwine('ss').removeForm();
// }
// } else {
// jQuery('.cms-edit-form').entwine('ss').removeForm();
// }
// close panel
// TODO Coupling with tabs
// jQuery('#TreeActions').tabs('select', -1);
}, },
dataType: 'json' dataType: 'json'
}); });
@ -301,11 +291,16 @@
onchange: function(e) { onchange: function(e) {
var form = $(e.target.form), btn = form.find(':submit'); var form = $(e.target.form), btn = form.find(':submit');
if($(e.target).val() == -1) { if($(e.target).val() == -1) {
btn.attr('disabled', 'disabled'); btn.attr('disabled', 'disabled').button('refresh');
} else { } else {
btn.removeAttr('disabled'); btn.removeAttr('disabled').button('refresh');
form.entwine('ss').refreshSelected(); // form.submit();
} }
// TODO Should work by triggering change() along, but doesn't - entwine event bubbling?
this.trigger("liszt:updated");
this._super(e);
} }
}); });

View File

@ -159,16 +159,22 @@
self.submitForm_responseHandler(form, xmlhttp.responseText, status, xmlhttp, formData); self.submitForm_responseHandler(form, xmlhttp.responseText, status, xmlhttp, formData);
} }
// Simulates a redirect on an ajax response - just exchange the URL without re-requesting it
if(window.History.enabled) {
var url = xmlhttp.getResponseHeader('X-ControllerURL');
if(url) window.history.replaceState({}, '', url);
}
// Re-init tabs (in case the form tag itself is a tabset) // Re-init tabs (in case the form tag itself is a tabset)
if(self.hasClass('ss-tabset')) self.removeClass('ss-tabset').addClass('ss-tabset'); if(self.hasClass('ss-tabset')) self.removeClass('ss-tabset').addClass('ss-tabset');
// Redraw the layout
jQuery('.cms-container').entwine('ss').redraw();
// re-select previously saved tabs // re-select previously saved tabs
$.each(selectedTabs, function(i, selectedTab) { $.each(selectedTabs, function(i, selectedTab) {
form.find('#' + selectedTab.id).tabs('select', selectedTab.selected); form.find('#' + selectedTab.id).tabs('select', selectedTab.selected);
}); });
// Redraw the layout
$('.cms-container').redraw();
}, },
dataType: 'html' dataType: 'html'
}, ajaxOptions)); }, ajaxOptions));
@ -296,21 +302,4 @@
} }
}); });
/**
* Loads the link's 'href' attribute into a panel via ajax,
* as opposed to triggering a full page reload.
* Little helper to avoid repetition, and make it easy to
* "opt in" to panel loading, while by default links still exhibit their default behaviour.
* Same goes for breadcrumbs in the CMS.
*/
$('.cms-content .cms-panel-link, .cms-content a.crumb').entwine({
onclick: function(e) {
var href = this.attr('href'), url = href ? href : this.data('href'),
data = (this.data('targetPanel')) ? {selector: this.data('targetPanel')} : null;
$('.cms-container').entwine('ss').loadPanel(url, null, data);
e.preventDefault();
}
});
})(jQuery); })(jQuery);

View File

@ -2,6 +2,14 @@
* File: LeftAndMain.EditForm.js * File: LeftAndMain.EditForm.js
*/ */
(function($) { (function($) {
// Can't bind this through jQuery
window.onbeforeunload = function(e) {
var form = $('.cms-edit-form');
form.trigger('beforesave');
if(form.is('.changed')) return ss.i18n._t('LeftAndMain.CONFIRMUNSAVEDSHORT');
};
$.entwine('ss', function($){ $.entwine('ss', function($){
/** /**
@ -52,12 +60,6 @@
this._setupChangeTracker(); this._setupChangeTracker();
// Can't bind this through jQuery
window.onbeforeunload = function(e) {
self.trigger('beforesave');
if(self.is('.changed')) return ss.i18n._t('LeftAndMain.CONFIRMUNSAVEDSHORT');
};
// Catch navigation events before they reach handleStateChange(), // Catch navigation events before they reach handleStateChange(),
// in order to avoid changing the menu state if the action is cancelled by the user // in order to avoid changing the menu state if the action is cancelled by the user
// $('.cms-menu') // $('.cms-menu')
@ -179,6 +181,7 @@
*/ */
onclick: function(e) { onclick: function(e) {
$('.cms-content').submitForm(this.parents('form'), this); $('.cms-content').submitForm(this.parents('form'), this);
e.preventDefault();
return false; return false;
} }
}); });

View File

@ -13,6 +13,9 @@
onmatch: function() { onmatch: function() {
this._super(); this._super();
// Don't reapply (expensive) tree behaviour if already present
if(!$.isNaN(this.data('jstree_instance_id'))) return;
var hints = this.attr('data-hints'); var hints = this.attr('data-hints');
if(hints) this.setHints($.parseJSON(hints)); if(hints) this.setHints($.parseJSON(hints));
@ -90,6 +93,9 @@
"drop_target" : false, "drop_target" : false,
"drag_target" : false "drag_target" : false
}, },
'checkbox': {
'two_state': true
},
'themes': { 'themes': {
'theme': 'apple', 'theme': 'apple',
'url': 'sapphire/thirdparty/jstree/themes/apple/style.css' 'url': 'sapphire/thirdparty/jstree/themes/apple/style.css'
@ -183,7 +189,7 @@
* DOMElement * DOMElement
*/ */
getNodeByID: function(id) { getNodeByID: function(id) {
return this.jstree('get_node', this.find('*[data-id='+id+']')); return this.find('*[data-id='+id+']');
}, },
/** /**
@ -201,7 +207,7 @@
return false; return false;
}; };
var handledform = $(e.target).children('.cms-edit-form:first')[0]; var handledform = $(e.target).is('.cms-edit-form') ? $(e.target)[0] : $(e.target).find('.cms-edit-form')[0];
var id = $(handledform.ID).val(); var id = $(handledform.ID).val();
// check if a form with a valid ID exists // check if a form with a valid ID exists
@ -254,75 +260,77 @@
} }
} }
},
onunmatch: function() {
} }
}); });
$('.cms-tree.multiple').entwine({
onmatch: function() {
this._super();
this.jstree('show_checkboxes');
},
onunmatch: function() {
this._super();
this.jstree('uncheck_all');
this.jstree('hide_checkboxes');
},
/**
* Function: getSelectedIDs
*
* Returns:
* (Array)
*/
getSelectedIDs: function() {
return $.map($(this).jstree('get_checked'), function(el, i) {return $(el).data('id');});
}
});
$('.cms-tree li').entwine({
/**
* Function: setEnabled
*
* Parameters:
* (bool)
*/
setEnabled: function(bool) {
this.toggleClass('disabled', !(bool));
},
/**
* Function: getClassname
*
* Returns PHP class for this element. Useful to check business rules like valid drag'n'drop targets.
*/
getClassname: function() {
var matches = this.attr('class').match(/class-([^\s]*)/i);
return matches ? matches[1] : '';
},
/**
* Function: getID
*
* Returns:
* (Number)
*/
getID: function() {
return this.data('id');
}
});
$('.cms-tree-view-modes input.view-mode').entwine({
onmatch: function() {
// set active by default
this.trigger('click');
this._super();
},
onclick: function(e) {
$('.cms-tree')
.toggleClass('draggable', $(e.target).val() == 'draggable')
.toggleClass('multiple', $(e.target).val() == 'multiselect');
}
});
}); });
$('.cms-tree.multiple').entwine({
onmatch: function() {
this._super();
this.jstree('show_checkboxes');
},
onunmatch: function() {
this._super();
this.jstree('uncheck_all');
this.jstree('hide_checkboxes');
},
/**
* Function: getSelectedIDs
*
* Returns:
* (Array)
*/
getSelectedIDs: function() {
return $.map($(this).jstree('get_checked'), function(el, i) {return $(el).data('id');});
}
});
$('.cms-tree li').entwine({
/**
* Function: setEnabled
*
* Parameters:
* (bool)
*/
setEnabled: function(bool) {
this.toggleClass('disabled', !(bool));
},
/**
* Function: getClassname
*
* Returns PHP class for this element. Useful to check business rules like valid drag'n'drop targets.
*/
getClassname: function() {
var matches = this.attr('class').match(/class-([^\s]*)/i);
return matches ? matches[1] : '';
},
/**
* Function: getID
*
* Returns:
* (Number)
*/
getID: function() {
return this.data('id');
}
});
$('.cms-tree-view-modes input.view-mode').entwine({
onmatch: function() {
// set active by default
this.trigger('click');
this._super();
},
onclick: function(e) {
$('.cms-tree').toggleClass('draggable', $(e.target).val() == 'draggable');
}
});
}(jQuery)); }(jQuery));

View File

@ -95,7 +95,7 @@ jQuery.noConflict();
redraw: function() { redraw: function() {
// Move from inner to outer layouts. Some of the elements might not exist. // Move from inner to outer layouts. Some of the elements might not exist.
// Not all edit forms are layouted, so qualify by their data value. // Not all edit forms are layouted, so qualify by their data value.
this.find('.cms-content-fields[data-layout-type]').redraw();
this.find('.cms-edit-form[data-layout-type]').redraw(); this.find('.cms-edit-form[data-layout-type]').redraw();
// Only redraw preview if its visible // Only redraw preview if its visible
@ -250,6 +250,12 @@ jQuery.noConflict();
} }
}); });
$('.cms-content-fields').entwine({
redraw: function() {
this.layout();
}
});
/** /**
* Make all buttons "hoverable" with jQuery theming. * Make all buttons "hoverable" with jQuery theming.
* Also sets the clicked button on a form submission, making it available through * Also sets the clicked button on a form submission, making it available through
@ -271,6 +277,45 @@ jQuery.noConflict();
} }
}); });
/**
* Loads the link's 'href' attribute into a panel via ajax,
* as opposed to triggering a full page reload.
* Little helper to avoid repetition, and make it easy to
* "opt in" to panel loading, while by default links still exhibit their default behaviour.
* Same goes for breadcrumbs in the CMS.
*/
$('.cms .cms-panel-link, .cms a.crumb').entwine({
onclick: function(e) {
var href = this.attr('href'), url = href ? href : this.data('href'),
data = (this.data('targetPanel')) ? {selector: this.data('targetPanel')} : null;
$('.cms-container').loadPanel(url, null, data);
e.preventDefault();
}
});
/**
* Does an ajax loads of the link's 'href' attribute via ajax and displays any FormResponse messages from the CMS.
* Little helper to avoid repetition, and make it easy to trigger actions via a link,
* without reloading the page, changing the URL, or loading in any new panel content.
*/
$('.cms .cms-link-ajax').entwine({
onclick: function(e) {
var href = this.attr('href'), url = href ? href : this.data('href');
jQuery.ajax({
url: url,
// Ensure that form view is loaded (rather than whole "Content" template)
complete: function(xmlhttp, status) {
var msg = (xmlhttp.getResponseHeader('X-Status')) ? xmlhttp.getResponseHeader('X-Status') : xmlhttp.responseText;
if (typeof msg != "undefined" && msg != null) eval(msg);
},
dataType: 'html'
});
e.preventDefault();
}
});
/** /**
* Trigger dialogs with iframe based on the links href attribute (see ssui-core.js). * Trigger dialogs with iframe based on the links href attribute (see ssui-core.js).
*/ */
@ -363,10 +408,15 @@ jQuery.noConflict();
* we can fix the height cropping. * we can fix the height cropping.
*/ */
$('.cms .field.dropdown, .cms .field.checkboxset').entwine({ $('.cms .field.dropdown select, .cms .field select[multiple]').entwine({
onmatch: function() { onmatch: function() {
$(this).find("select:not(.no-chzn)").data('placeholder', ' ').chosen(); if(this.is('.no-chzn')) return;
$(this).addClass("has-chzn");
// Explicitly disable default placeholder if no custom one is defined
if(!this.data('placeholder')) this.data('placeholder', ' ');
// Apply chosen
this.chosen().addClass("has-chzn");
this._super(); this._super();
} }
@ -410,7 +460,10 @@ jQuery.noConflict();
}); });
var url = this.attr('action'); var url = this.attr('action');
if(nonEmptyInputs.length) url += '?' + nonEmptyInputs.serialize(); if(nonEmptyInputs.length) url += '?' + nonEmptyInputs.serialize();
this.closest('.cms-container').entwine('ss').loadPanel(url);
var container = this.closest('.cms-container');
container.find('.cms-edit-form').tabs('select',0); //always switch to the first tab (list view) when searching
container .entwine('ss').loadPanel(url);
return false; return false;
}, },
@ -423,6 +476,23 @@ jQuery.noConflict();
} }
}); });
/**
* Simple toggle link, which points to a DOm element by its ID selector
* in the href attribute (which doubles as an anchor link to that element).
*/
$('.cms .cms-help-toggle').entwine({
onmatch: function() {
this._super();
$(this.attr('href')).hide();
},
onclick: function(e) {
$(this.attr('href')).toggle();
e.preventDefault();
}
});
}(jQuery)); }(jQuery));
var statusMessage = function(text, type) { var statusMessage = function(text, type) {

View File

@ -1,347 +0,0 @@
/**
* File: MemberTableField.js
*/
(function($) {
$.entwine('ss', function($){
/**
* Class: #Permissions .checkbox[value=ADMIN]
*
* Automatically check and disable all checkboxes if ADMIN permissions are selected.
* As they're disabled, any changes won't be submitted (which is intended behaviour),
* checking all boxes is purely presentational.
*/
$('#Permissions .checkbox[value=ADMIN]').entwine({
onmatch: function() {
this.toggleCheckboxes();
this._super();
},
/**
* Function: onclick
*/
onclick: function(e) {
this.toggleCheckboxes();
},
/**
* Function: toggleCheckboxes
*/
toggleCheckboxes: function() {
var self = this, checkboxes = this.parents('.field:eq(0)').find('.checkbox').not(this);
if(this.is(':checked')) {
checkboxes.each(function() {
$(this).data('SecurityAdmin.oldChecked', $(this).attr('checked'));
$(this).data('SecurityAdmin.oldDisabled', $(this).attr('disabled'));
$(this).attr('disabled', 'disabled');
$(this).attr('checked', 'checked');
});
} else {
checkboxes.each(function() {
$(this).attr('checked', $(this).data('SecurityAdmin.oldChecked'));
$(this).attr('disabled', $(this).data('SecurityAdmin.oldDisabled'));
});
}
}
});
});
}(jQuery));
/**
* Modified 2006-10-05, Ingo Schommer
* This is more or less a copy of Member.js, with additions and changes
* to match the switch from Member.php to MemberTableField.php all over the UI.
* Eventually it will replace Member.js (please remove this message then).
*/
// no confirm message for removal from a group
if(typeof(ComplexTableField) != 'undefined') {
ComplexTableField.prototype.deleteConfirmMessage = null;
}
/**
* Class: AjaxMemberLookup
*
* Auto-lookup on ajax fields
*/
AjaxMemberLookup = {
initialise : function() {
var div = document.createElement('div');
div.id = this.id + '_ac';
div.className = 'autocomplete';
this.parentNode.appendChild(div);
if(this.id) {
new Ajax.Autocompleter(this.id, div.id, 'admin/security/autocomplete/' + this.name, {
afterUpdateElement : this.afterAutocomplete.bind(this)
});
}
},
afterAutocomplete : function(field, selectedItem) {
var items = jQuery(selectedItem).data('fields'), form = jQuery(selectedItem).parents('form:first');
for(name in items) {
jQuery(form).find('input[name='+name+']').val(items[name]);
}
}
}
/**
* Class: MemberTableField
*/
MemberTableField = Class.create();
MemberTableField.applyTo('.cms-edit-form div.MemberTableField');
MemberTableField.prototype = {
initialize: function() {
Behaviour.register({
'.cms-edit-form div.MemberFilter input' : {
onkeypress : this.prepareSearch.bind(this)
},
'.cms-edit-form div.MemberTableField table.data tr.addtogrouprow input' : {
onkeypress : this.prepareAddToGroup.bind(this)
},
'.cms-edit-form div.MemberTableField table.data tr.addtogrouprow #Form_AddRecordForm_action_addtogroup' : {
onclick : this.prepareAddToGroup.bind(this)
},
'.cms-edit-form div.MemberTableField table.data tr.addtogrouprow td.actions input' : {
initialise: function() {
data = this.parentNode.parentNode.getElementsByTagName('input');
var i,item,error = [];
for(i=0;item=data[i];i++) {
item.originalSerialized = Form.Element.serialize(item);
}
},
onclick : this.addToGroup.bind(this)
},
//'.cms-edit-form div.MemberTableField input' : AjaxMemberLookup,
'.cms-edit-form' : {
changeDetection_fieldsToIgnore : {
'ctf[start]' : true,
'ctf[ID]' : true,
'MemberOrderByField' : true,
'MemberOrderByOrder' : true,
'MemberGroup' : true,
'MemberFilterButton' : true,
'MemberFieldName' : true,
'MemberDontShowPassword' : true,
'MemberSearch' : true
}
}
});
},
// prevent submission of wrong form-button (MemberFilterButton)
prepareAddToGroup: function(e) {
// IE6 doesnt send an event-object with onkeypress
var event = (e) ? e : window.event;
var keyCode = (event.keyCode) ? event.keyCode : event.which;
if(keyCode == Event.KEY_RETURN) {
var el = Event.element(event);
this.addToGroup(event);
Event.stop(event);
return false;
}
},
// prevent submission of wrong form-button (MemberFilterButton)
prepareSearch: function(e) {
// IE6 doesnt send an event-object with onkeypress
var event = (e) ? e : window.event;
var keyCode = (event.keyCode) ? event.keyCode : event.which;
if(keyCode == Event.KEY_RETURN) {
var el = Event.element(event);
$('MemberFilterButton').onclick(event);
Event.stop(event);
return false;
}
},
addToGroup: function(e) {
// only submit parts of the form
var data = this.parentNode.parentNode.getElementsByTagName('input');
var i,item,error = [];
var form = Event.findElement(e,"form");
for(i=0;item=data[i];i++) {
if(item.name == 'Email' && !item.value) error[error.length] = "Email";
if(item.name == 'Password' && !item.value) error[error.length] = "Password";
}
if(error.length > 0) {
alert('Please enter a ' + error.join(' and a ') + ' to add a member.');
} else {
updateURL = "";
updateURL += Event.findElement(e,"form").action;
// we can't set "fieldName" as a HiddenField because there might be multiple ComplexTableFields in a single EditForm-container
updateURL += "?fieldName="+$('MemberFieldName').value;
updateURL += "&action_callfieldmethod&methodName=addtogroup";
ajaxSubmitFieldSet(updateURL, data);
}
return false;
}
/*
initialise : function() {
this.headerMap = [];
var i, item, headers = this.getElementsByTagName('thead')[0].getElementsByTagName('tr')[0].getElementsByTagName('td');
for(i=0;item=headers[i];i++) {
this.headerMap[i] = item.className;
}
},
setRecordDetails : function(id, details, groupID) {
var row = document.getElementById('member-' + id);
if(row) {
var i, item, cells = row.getElementsByTagName('td');
for(i=0;item=cells[i];i++) {
if(details[this.headerMap[i]]) {
item.innerHTML = details[this.headerMap[i]];
}
}
} else {
this.createRecord(id, details, groupID);
}
},
createRecord : function (id, details, groupId) {
var row = document.createElement('tr');
row.id = 'member-' + id;
var i, cell, cellField;
for(i=0;cellField=this.headerMap[i];i++) {
cell = document.createElement('td')
if(details[cellField]) {
cell.innerHTML = details[cellField];
}
row.appendChild(cell);
}
// Add the delete icon
if(typeof groupId == 'undefined')
var groupId = $('Form_EditForm').elements.ID.value;
cell = document.createElement('td')
cell.innerHTML = '<a class="deletelink" href="admin/security/removememberfromgroup/' + groupId + '/' + id + '"><img src="sapphire/images/delete.gif" alt="delete" /></a>';
cell.getElementsByTagName('0');
row.appendChild(cell);
var tbody = this.getElementsByTagName('tbody')[0];
var addRow = document.getElementsByClassName('addrow',tbody)[0];
if(addRow) tbody.insertBefore(row, addRow);
else tbody.appendChild(row);
Behaviour.apply(row, true);
},
clearAddForm : function() {
var tbody = this.getElementsByTagName('tbody')[0];
var addRow = document.getElementsByClassName('addrow',tbody)[0];
if(addRow) {
var i,field,fields = addRow.getElementsByTagName('input');
for(i=0;field=fields[i];i++) {
if(field.type != 'hidden' && field.type != 'submit') field.value = '';
}
}
},
removeMember : function(memberID) {
var record;
if(record = $('member-' + memberID)) {
record.parentNode.removeChild(record);
}
}
*/
}
/**
* Class: MemberFilterButton
*/
MemberFilterButton = Class.create();
MemberFilterButton.applyTo('#MemberFilterButton');
MemberFilterButton.prototype = {
initialize: function() {
this.inputFields = new Array();
var childNodes = this.parentNode.parentNode.getElementsByTagName('input');
for( var index = 0; index < childNodes.length; index++ ) {
if( childNodes[index].tagName ) {
childNodes[index].resetChanged = function() { return false; }
childNodes[index].isChanged = function() { return false; }
this.inputFields.push( childNodes[index] );
}
}
childNodes = this.parentNode.getElementsByTagName('select');
for( var index = 0; index < childNodes.length; index++ ) {
if( childNodes[index].tagName ) {
childNodes[index].resetChanged = function() { return false; }
childNodes[index].field_changed = function() { return false; }
this.inputFields.push( childNodes[index] );
}
}
},
isChanged: function() {
return false;
},
onclick: function(e) {
if(!$('ctf-ID') || !$('MemberFieldName')) {
return false;
}
try {
var form = Event.findElement(e,"form");
var fieldName = $('MemberFieldName').value;
var fieldID = form.id + '_' + fieldName;
var updateURL = form.action + '/field/' + fieldName + '?ajax=1';
for( var index = 0; index < this.inputFields.length; index++ ) {
if( this.inputFields[index].tagName ) {
updateURL += '&' + this.inputFields[index].name + '=' + encodeURIComponent( this.inputFields[index].value );
}
}
jQuery($(fieldID)).get(updateURL, null, function() {Behaviour.apply($(fieldID), true);});
} catch(er) {
errorMessage('Error searching');
}
return false;
}
}
// has to be external from initialize() because otherwise request will double on each reload - WTF
Behaviour.register({
'.cms-edit-form div.MemberTableField table.data input.text' : AjaxMemberLookup
});
/**
* Post the given fields to the given url
*/
function ajaxSubmitFieldSet(href, fieldSet, extraData) {
// Build data
var i,field,data = "ajax=1";
for(i=0;field=fieldSet[i];i++) {
data += '&' + Form.Element.serialize(field);
}
if(extraData){
data += '&'+extraData;
}
// Send request
jQuery.ajax({
'url': href,
'method' : 'post',
'data' : data,
'success' : function(response) {
eval(response);
},
'error' : function(response) {
alert(response.responseText);
}
});
}

View File

@ -1,21 +0,0 @@
/**
* File: MemberTableField_popup.js
*/
/**
* Class: MemberTableFieldPopupForm
*/
MemberTableFieldPopupForm = Class.extend("ComplexTableFieldPopupForm");
MemberTableFieldPopupForm.prototype = {
initialize: function() {
this.ComplexTableFieldPopupForm.initialize();
Behaviour.register('MemberTableFieldPopupForm',{
"div.MemberTableField_Popup .Actions input.action": {
onclick: this.submitForm.bind(this)
}
});
}
}
MemberTableFieldPopupForm.applyTo('div.MemberTableField_Popup .Actions');

View File

@ -30,42 +30,47 @@
$(this).bind('load', refreshAfterImport); $(this).bind('load', refreshAfterImport);
} }
}); });
})
/**
* Delete selected folders through "batch actions" tab.
*/
$(document).ready(function() {
$('#Form_BatchActionsForm').entwine('ss').register(
// TODO Hardcoding of base URL
'admin/security/batchactions/delete',
function(ids) {
var confirmed = confirm(
ss.i18n.sprintf(
ss.i18n._t('SecurityAdmin.BATCHACTIONSDELETECONFIRM'),
ids.length
)
);
return (confirmed) ? ids : false;
}
);
}); });
$.entwine('ss', function($){ $.entwine('ss', function($){
/** /**
* Class: .cms-edit-form .Actions #Form_EditForm_action_addmember * Class: #Permissions .checkbox[value=ADMIN]
*
* Automatically check and disable all checkboxes if ADMIN permissions are selected.
* As they're disabled, any changes won't be submitted (which is intended behaviour),
* checking all boxes is purely presentational.
*/ */
$('.cms-edit-form .Actions #Form_EditForm_action_addmember').entwine({ $('#Permissions .checkbox[value=ADMIN]').entwine({
// Function: onclick onmatch: function() {
this.toggleCheckboxes();
this._super();
},
/**
* Function: onclick
*/
onclick: function(e) { onclick: function(e) {
// CAUTION: Assumes that a MemberTableField-instance is present as an editing form this.toggleCheckboxes();
var t = $('#Form_EditForm_Members'); },
t[0].openPopup( /**
null, * Function: toggleCheckboxes
$('base').attr('href') + t.find('a.addlink').attr('href'), */
t.find('table')[0] toggleCheckboxes: function() {
); var self = this, checkboxes = this.parents('.field:eq(0)').find('.checkbox').not(this);
return false;
if(this.is(':checked')) {
checkboxes.each(function() {
$(this).data('SecurityAdmin.oldChecked', $(this).attr('checked'));
$(this).data('SecurityAdmin.oldDisabled', $(this).attr('disabled'));
$(this).attr('disabled', 'disabled');
$(this).attr('checked', 'checked');
});
} else {
checkboxes.each(function() {
$(this).attr('checked', $(this).data('SecurityAdmin.oldChecked'));
$(this).attr('disabled', $(this).data('SecurityAdmin.oldDisabled'));
});
}
} }
}); });
}); });

View File

@ -29,7 +29,7 @@ body.cms {
.cms { .cms {
a { a {
color: $color-text-dark-link; color: $color-text-blue-link;
text-decoration: none; text-decoration: none;
&:hover, &:hover,
@ -80,7 +80,6 @@ body.cms {
@include inline-block; @include inline-block;
} }
.cms-content-header { .cms-content-header {
padding: ($grid-y - 8) $grid-x $grid-y; padding: ($grid-y - 8) $grid-x $grid-y;
height: $grid-y * 4; height: $grid-y * 4;
@ -98,6 +97,9 @@ body.cms {
.backlink { .backlink {
float:left; float:left;
margin-top:$grid-x - 1; margin-top:$grid-x - 1;
span.btn-icon-back {
height:15px;
}
} }
h2 { h2 {
@ -126,17 +128,6 @@ body.cms {
font-weight: bold; font-weight: bold;
line-height: $grid-y * 2; line-height: $grid-y * 2;
padding: ($grid-y * 2 - 4) ($grid-x * 2 + 4) $grid-y + 3; padding: ($grid-y * 2 - 4) ($grid-x * 2 + 4) $grid-y + 3;
text-indent:-9999em;
&.content-treeview {
background:url(../images/content-header-tabs-sprite.png) no-repeat 2px 0px;
}
&.content-galleryview {
background:url(../images/content-header-tabs-sprite.png) no-repeat -87px 0px;
}
&.content-listview {
background:url(../images/content-header-tabs-sprite.png) no-repeat -38px 0px;
}
} }
} }
@ -154,18 +145,6 @@ body.cms {
border: { border: {
top:none; top:none;
} }
a {
&.content-treeview {
background:url(../images/content-header-tabs-sprite.png) no-repeat 2px -40px;
}
&.content-galleryview {
background:url(../images/content-header-tabs-sprite.png) no-repeat -87px -40px;
}
&.content-listview {
background:url(../images/content-header-tabs-sprite.png) no-repeat -38px -40px;
}
}
} }
.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { .ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr {
border-radius:0; border-radius:0;
@ -245,6 +224,51 @@ body.cms {
} }
} }
.CMSPagesController {
.cms-content-header-tabs {
.ui-tabs-nav {
li {
a {
font-weight: bold;
line-height: $grid-y * 2;
padding: ($grid-y * 2 - 4) ($grid-x * 2 + 4) $grid-y + 3;
text-indent:-9999em;
&.content-treeview {
background:url(../images/content-header-tabs-sprite.png) no-repeat 2px 0px;
}
&.content-galleryview {
background:url(../images/content-header-tabs-sprite.png) no-repeat -87px 0px;
}
&.content-listview {
background:url(../images/content-header-tabs-sprite.png) no-repeat -38px 0px;
}
}
}
.ui-state-active,
.ui-widget-content .ui-state-active,
.ui-widget-header .ui-state-active {
border: {
top:none;
}
a {
&.content-treeview {
background:url(../images/content-header-tabs-sprite.png) no-repeat 2px -40px;
}
&.content-galleryview {
background:url(../images/content-header-tabs-sprite.png) no-repeat -87px -40px;
}
&.content-listview {
background:url(../images/content-header-tabs-sprite.png) no-repeat -38px -40px;
}
}
}
}
}
}
/** ------------------------------------------------------- /** -------------------------------------------------------
* Loading Interface * Loading Interface
* ------------------------------------------------------- */ * ------------------------------------------------------- */
@ -356,7 +380,7 @@ body.cms {
background-color: lighten($color-warning, 20%); background-color: lighten($color-warning, 20%);
border-color: $color-warning; border-color: $color-warning;
} }
&.error { &.error, &.bad, &.required {
background-color: lighten($color-error, 20%); background-color: lighten($color-error, 20%);
border-color: $color-error; border-color: $color-error;
} }
@ -688,9 +712,6 @@ body.cms {
form.cms-batch-actions { form.cms-batch-actions {
float: left; float: left;
} }
.Actions {
display:none;
}
} }
#Form_BatchActionsForm select { #Form_BatchActionsForm select {
@ -885,6 +906,28 @@ form.member-profile-form {
overflow-x: auto; overflow-x: auto;
background: none; background: none;
width:100%; width:100%;
.aligned_right_label {
margin-left:$grid-x*23;
padding:$grid-x 0;
}
}
}
/** --------------------------------------------
* "Settings" Form
* -------------------------------------------- */
#CanViewType, #CanEditType, #CanCreateTopLevelType {
.optionset li {
// All options on their own line
float: none;
width: auto;
}
}
#ViewerGroups, #EditorGroups, #CreateTopLevelGroups {
select {
// Fix for chosen.js width detection on hidden elements.
// TODO Remove once .field styling in _form.scss is refactored to allow flexible field widths
width: $grid-x * 64;
} }
} }
@ -1015,11 +1058,13 @@ form.member-profile-form {
} }
.cms-panel-padded { .cms {
width: ($grid-x * 22); .cms-panel-padded {
padding: $grid-y*2 $grid-x*2; width: ($grid-x * 22);
overflow-x: hidden; padding: $grid-y*2 $grid-x*2;
overflow-y: auto; overflow-x: hidden;
overflow-y: auto;
}
} }
/** ------------------------------------------------------------------ /** ------------------------------------------------------------------
@ -1296,3 +1341,22 @@ form.small {
} }
} }
} }
/** --------------------------------------------
* Users Members Admin
* -------------------------------------------- */
.members_grid {
span button#action_gridfield_relationfind {
display:none;
}
p button#action_export {
margin-top:$grid-y*2;
span.btn-icon-download-csv {
height:$grid-y*2 + 1;
}
span.ui-button-text {
padding-left:$grid-x*3 + 3;
}
}
}

View File

@ -380,6 +380,17 @@
.jstree li { .jstree li {
line-height: 25px; line-height: 25px;
} }
// Hide drag icons by default. Actual page icons
// are in nested <span>, not handled directly through jstree lib
a > .jstree-icon {
display: none;
}
// Show drag icons when draggable class is applied
.draggable a > .jstree-icon {
display: block;
}
} }
.jstree-apple { .jstree-apple {
@ -519,7 +530,7 @@
background-image: url(../images/sitetree_ss_default_icons.png); background-image: url(../images/sitetree_ss_default_icons.png);
} }
& li.jstree-checked a, li.jstree-checked a:link { & li.jstree-checked > a, li.jstree-checked > a:link {
background-color: $color-cms-batchactions-menu-selected-background; background-color: $color-cms-batchactions-menu-selected-background;
} }
@ -532,10 +543,6 @@
} }
} }
.jstree-apple #record-0.jstree-open > ins {
display: none;
}
a .jstree-pageicon { a .jstree-pageicon {
display: block; display: block;
float: left; float: left;
@ -565,4 +572,18 @@ li.class-ErrorPage > a .jstree-pageicon {
.cms-tree { .cms-tree {
visibility: hidden; // enabled by JS to avoid layout glitches visibility: hidden; // enabled by JS to avoid layout glitches
&.multiple {
// Hide draggable icon when multiselect is enabled
li > a > .jstree-icon {
display: none;
}
li#record-0 {
// Hide checkbox on root node (shouldn't be selectable to avoid weird states when trying to e.g. remove it)
&> a .jstree-checkbox {
display: none;
}
}
}
} }

View File

@ -53,16 +53,116 @@ html {
text-indent: 0px !important; text-indent: 0px !important;
} }
.cms { //fix for filter and reset icons on datagrid
table.ss-gridfield-table {
tbody { .cms table.ss-gridfield-table tr th.extra span input {
td { height:23px;
button { }
span.ui-button-text {
zoom:1; .ss-gridfield-button-filter.ss-ui-button {
} margin: -1px -5px;
} background: #e6e6e6 url(../images/filter-icons.png) no-repeat -40px 6px; filter:none;
&.hover-alike:active {
background: darken(#338DC1, 5%) url(../images/filter-icons.png) no-repeat -15px 7px; filter:none;
}
&.hover-alike {
background: #338DC1 url(../images/filter-icons.png) no-repeat -16px 6px; filter:none;
}
}
.ss-gridfield-button-reset.ss-ui-button {
margin: -1px -5px;
background: #e6e6e6 url(../images/filter-icons.png) no-repeat 8px 5px; filter:none;
&.filtered:hover {
background: #FF0000 url(../images/filter-icons.png) no-repeat 8px -17px; filter:none;
}
&.filtered:active {
background: darken(#FF0000, 5%) url(../images/filter-icons.png) no-repeat 9px -16px; filter:none;
}
}
//fix for borders on gridfield table
.cms table.ss-gridfield-table {
tr {
td {
border-right: 1px solid lighten(#808080, 10%);
}
th {
border-right: 1px solid lighten(#808080, 10%);
&.main {
border-top: 1px solid lighten(#808080, 10%);
border-bottom: none;
}
&.extra {
border-top: 1px solid lighten(#808080, 10%);
padding-right:12px;
} }
} }
} }
td:first-child, th:first-child {
border-left: 1px solid lighten(#808080, 10%);
}
}
//fix for edit and delete icons in IE7
.cms .ss-gridfield table.ss-gridfield-table tbody {
td {
button {
&.gridfield-button-delete {
display:block;
float:left;
}
&.gridfield-button-unlink.ui-state-hover {
display:block;
float:left;
}
}
a.edit-link {
display:block;
float:left;
}
}
}
//fix for wrong alignment of label on parent groups field in users groups and fix for width of parent group dropdown field
.cms .cms-content .cms-content-fields {
.aligned_right_label {
margin-left:0;
}
.field.dropdown .middleColumn {
max-width:512px;
}
}
//fix for input on datagrid pagination
.pagination-page-number {
position:relative;
bottom:10px;
right:10px;
input {
width:45px;
padding:0px;
position:relative;
bottom:2px;
}
}
//fix for wrong height on users groups gridfield table header
table.ss-gridfield-table tr.title th h2 {
float:left;
}
//fix for alternate colors on rows in datagrid
table.ss-gridfield-table tr {
&.ss-gridfield-item.odd {
background: white;
}
&.ss-gridfield-item.even {
background: #F0F4F7;
}
} }

View File

@ -28,3 +28,53 @@
.ui-widget-header .ui-state-default { .ui-widget-header .ui-state-default {
background-image:none; background-image:none;
} }
//fix for filter and reset icons on datagrid
.ss-gridfield-button-filter.ss-ui-button{
background: #e6e6e6 url(../images/filter-icons.png) no-repeat -40px 6px; filter:none;
&.hover-alike:active {
background: darken(#338DC1, 5%) url(../images/filter-icons.png) no-repeat -15px 7px; filter:none;
}
&.hover-alike {
background: #338DC1 url(../images/filter-icons.png) no-repeat -16px 6px; filter:none;
}
}
.ss-gridfield-button-reset.ss-ui-button{
background: #e6e6e6 url(../images/filter-icons.png) no-repeat 8px 5px; filter:none;
&.filtered:hover {
background: #FF0000 url(../images/filter-icons.png) no-repeat 8px -17px; filter:none;
}
&.filtered:active {
background: darken(#FF0000, 5%) url(../images/filter-icons.png) no-repeat 9px -16px; filter:none;
}
}
//fix for borders on gridfield table
.cms table.ss-gridfield-table {
tr {
td {
border-right: 1px solid lighten(#808080, 10%);
}
th {
border-right: 1px solid lighten(#808080, 10%);
&.main {
border-top: 1px solid lighten(#808080, 10%);
border-bottom: none;
}
&.extra {
border-top: 1px solid lighten(#808080, 10%);
}
}
}
td:first-child, th:first-child {
border-left: 1px solid lighten(#808080, 10%);
}
}
//fix for wrong alignment of label on parent groups field in users groups
.cms .cms-content .cms-content-fields .aligned_right_label {
margin-left:0;
}

View File

@ -20,7 +20,7 @@
<div class="cms-content-header-tabs"> <div class="cms-content-header-tabs">
<ul> <ul>
<% control Tabs %> <% control Tabs %>
<li><a href="#$id">$Title</a></li> <li><a href="#$id"<% if extraClass %> class="$extraClass"<% end_if %>>$Title</a></li>
<% end_control %> <% end_control %>
</ul> </ul>
</div> </div>

View File

@ -1,62 +0,0 @@
<div id="$id" class="$CSSClasses $extraClass field" href="$CurrentLink">
<div class="MemberFilter filterBox">
$SearchForm
</div>
<div id="MemberList">
<% if Markable %>
<% include TableListField_SelectOptions %>
<% end_if %>
<% include TableListField_PageControls %>
<table class="data">
<thead>
<tr>
<% if Markable %><th width="18">&nbsp;</th><% end_if %>
<% control Headings %>
<th class="$Name">$Title</th>
<% end_control %>
<% if Can(show) %><th width="18">&nbsp;</th><% end_if %>
<% if Can(edit) %><th width="18">&nbsp;</th><% end_if %>
<% if Can(delete) %><th width="18">&nbsp;</th><% end_if %>
</tr>
</thead>
<tfoot>
<% if Can(inlineadd) %>
<tr class="addtogrouprow">
<% if Markable %><td width="18">&nbsp;</dh><% end_if %>
$AddRecordForm.CellFields
<td class="actions" colspan="3">$AddRecordForm.CellActions</td>
</tr>
<% end_if %>
<tr style="display: none;">
<% if Markable %><td width="18">&nbsp;</td><% end_if %>
<td colspan="$ItemCount">
<input type="hidden" id="{$id}_PopupHeight" value="$PopupHeight" disabled="disabled"/>
<input type="hidden" id="{$id}_PopupWidth" value="$PopupWidth" disabled="disabled"/>
<a class="popuplink addlink" href="$AddLink" alt="add"><img src="sapphire/images/add.gif" alt="add" /></a><a class="popuplink addlink" href="$AddLink" alt="add"><% _t('ADDNEW','Add new',50,'Followed by a member type') %> $Title</a>
</td>
<% if Can(show) %><td width="18">&nbsp;</td><% end_if %>
<% if Can(edit) %><td width="18">&nbsp;</td><% end_if %>
<% if Can(delete) %><td width="18">&nbsp;</td><% end_if %>
</tr>
</tfoot>
<tbody>
<% if Items %>
<% control Items %>
<% include TableListField_Item %>
<% end_control %>
<% else %>
<tr class="notfound">
<% if Markable %><th width="18">&nbsp;</th><% end_if %>
<td colspan="$Headings.Count"><i><% _t('NOITEMSFOUND', 'No items found') %></i></td>
<% control Actions %><td width="18">&nbsp;</td><% end_control %>
</tr>
<% end_if %>
</tbody>
</table>
<div class="utility">
<% control Utility %>
<span class="item"><a href="$Link" target="_blank">$Title</a></span>
<% end_control %>
</div>
</div>
</div>

View File

@ -1,9 +1,13 @@
<div class="cms-content center $BaseCSSClasses" data-layout-type="border"> <div class="cms-content center $BaseCSSClasses" data-layout-type="border">
<div class="cms-content-header north"> <div class="cms-content-header north">
<div> <div><h2>
<h2><% _t('ModelAdmin.Title', 'My Model') %></h2> <% if SectionTitle %>
</div> $SectionTitle
<% else %>
<% _t('ModelAdmin.Title', 'Data Models') %>
<% end_if %>
</h2></div>
</div> </div>
$Tools $Tools

View File

@ -6,7 +6,7 @@
<% if SearchClassSelector = tabs %> <% if SearchClassSelector = tabs %>
<ul> <ul>
<% control ModelForms %> <% control ModelForms %>
<li class="$FirstLast"><a id="tab-ModelAdmin_$Title.HTMLATT" href="#{$Form.Name}_$ClassName">$Title</a></li> <li class="$FirstLast"><a id="tab-ModelAdmin_$Title.HTMLATT" href="#Form_$ClassName">$Title</a></li>
<% end_control %> <% end_control %>
</ul> </ul>
<% end_if %> <% end_if %>
@ -16,14 +16,14 @@
Search for: Search for:
<select> <select>
<% control ModelForms %> <% control ModelForms %>
<option value="{$Form.Name}_$ClassName">$Title</option> <option value="Form_$ClassName">$Title</option>
<% end_control %> <% end_control %>
</select> </select>
</div> </div>
<% end_if %> <% end_if %>
<% control ModelForms %> <% control ModelForms %>
<div class="tab" id="{$Form.Name}_$ClassName"> <div class="tab" id="Form_$ClassName">
$Content $Content
</div> </div>
<% end_control %> <% end_control %>

View File

@ -1,21 +0,0 @@
<div class="cms-content center $BaseCSSClasses" data-layout-type="border">
<div class="cms-content-tools west">
<div class="cms-content-header north">
<div>
<h2>
<% include CMSBreadcrumbs %>
</h2>
</div>
</div>
$AddForm
<div data-url-tree="$Link(getsubtree)" data-url-savetreenode="$Link(savetreenode)" class="cms-tree draggable jstree jstree-apple">
$SiteTreeAsUL
</div>
</div>
$EditForm
</div>

View File

@ -1,7 +1,3 @@
<% if CreateForm %>
$CreateForm
<% end_if %>
<h3><% _t('SEARCHLISTINGS','Search') %></h3> <h3><% _t('SEARCHLISTINGS','Search') %></h3>
$SearchForm $SearchForm

View File

@ -1,139 +0,0 @@
<?php
/**
* @package cms
* @subpackage tests
*/
class MemberTableFieldTest extends FunctionalTest {
static $fixture_file = 'sapphire/admin/tests/MemberTableFieldTest.yml';
function testLimitsToMembersInGroup() {
$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",
$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 FieldList(new MemberTableField($this, "Members", $group1)),
new FieldList(new FormAction('submit'))
);
}
function FormNoGroup() {
$tf = new MemberTableField(
$this,
"Members"
// no group
);
return new Form(
$this,
'FormNoGroup',
new FieldList(new MemberTableField($this, "Members")),
new FieldList(new FormAction('submit'))
);
}
}

View File

@ -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

View File

@ -9,39 +9,41 @@ class SecurityAdminTest extends FunctionalTest {
protected $extraDataObjects = array('LeftAndMainTest_Object'); protected $extraDataObjects = array('LeftAndMainTest_Object');
function testGroupExport() { // TODO Fix export feature (moved from MemberTableField to GridFieldExporter)
$this->session()->inst_set('loggedInAs', $this->idFromFixture('Member', 'admin')); // function testGroupExport() {
// $this->session()->inst_set('loggedInAs', $this->idFromFixture('Member', 'admin'));
/* First, open the applicable group */ // /* First, open the applicable group */
$response = $this->get('admin/security/show/' . $this->idFromFixture('Group','admin')); // $response = $this->get('admin/security/show/' . $this->idFromFixture('Group','admin'));
$inputs = $this->cssParser()->getBySelector('input#Form_EditForm_Title'); // $inputs = $this->cssParser()->getBySelector('input#Form_EditForm_Title');
$this->assertNotNull($inputs); // $this->assertNotNull($inputs);
$this->assertEquals('Administrators', (string)$inputs[0]['value']); // $this->assertEquals('Administrators', (string)$inputs[0]['value']);
/* Then load the export page */ // /* Then load the export page */
$this->get('admin/security/EditForm/field/Members/export'); // $this->get('admin/security/EditForm/field/Members/export');
$lines = preg_split('/\n/', $this->content()); // $lines = preg_split('/\n/', $this->content());
$this->assertEquals(count($lines), 3, "Export with members has one content row"); // $this->assertEquals(count($lines), 3, "Export with members has one content row");
$this->assertRegExp('/"","","admin@example.com"/', $lines[1], "Member values are correctly exported"); // $this->assertRegExp('/"","","admin@example.com"/', $lines[1], "Member values are correctly exported");
} // }
function testEmptyGroupExport() { // TODO Fix export feature (moved from MemberTableField to GridFieldExporter)
$this->session()->inst_set('loggedInAs', $this->idFromFixture('Member', 'admin')); // function testEmptyGroupExport() {
// $this->session()->inst_set('loggedInAs', $this->idFromFixture('Member', 'admin'));
/* First, open the applicable group */ // /* First, open the applicable group */
$this->get('admin/security/show/' . $this->idFromFixture('Group','empty')); // $this->get('admin/security/show/' . $this->idFromFixture('Group','empty'));
$inputs = $this->cssParser()->getBySelector('input#Form_EditForm_Title'); // $inputs = $this->cssParser()->getBySelector('input#Form_EditForm_Title');
$this->assertNotNull($inputs); // $this->assertNotNull($inputs);
$this->assertEquals('Empty Group', (string)$inputs[0]['value']); // $this->assertEquals('Empty Group', (string)$inputs[0]['value']);
/* Then load the export page */ // /* Then load the export page */
$this->get('admin/security/EditForm/field/Members/export'); // $this->get('admin/security/EditForm/field/Members/export');
$lines = preg_split('/\n/', $this->content()); // $lines = preg_split('/\n/', $this->content());
$this->assertEquals(count($lines), 2, "Empty export only has header fields and an empty row"); // $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"); // $this->assertEquals($lines[1], '', "Empty export only has no content row");
} // }
function testAddHiddenPermission() { function testAddHiddenPermission() {
SecurityAdmin::add_hidden_permission('CMS_ACCESS_ReportAdmin'); SecurityAdmin::add_hidden_permission('CMS_ACCESS_ReportAdmin');
@ -74,7 +76,7 @@ class SecurityAdminTest extends FunctionalTest {
$group = $this->objFromFixture('Group', 'admin'); $group = $this->objFromFixture('Group', 'admin');
SecurityAdmin::add_hidden_permission('CMS_ACCESS_ReportAdmin'); SecurityAdmin::add_hidden_permission('CMS_ACCESS_ReportAdmin');
$response = $this->get('admin/security/show/' . $group->ID); $response = $this->get(sprintf('admin/security/EditForm/field/Groups/item/%d/edit', $group->ID));
$this->assertContains( $this->assertContains(
'CMS_ACCESS_SecurityAdmin', 'CMS_ACCESS_SecurityAdmin',

View File

@ -77,7 +77,8 @@ task 'build', 'build Chosen from source', build = (cb) ->
write_chosen_javascript javascript.replace(/\.js$/,'.min.js'), ( write_chosen_javascript javascript.replace(/\.js$/,'.min.js'), (
uglify.gen_code uglify.ast_squeeze uglify.ast_mangle parser.parse code uglify.gen_code uglify.ast_squeeze uglify.ast_mangle parser.parse code
) )
cb() if typeof cb is 'function' package_npm () ->
cb() if typeof cb is 'function'
catch e catch e
print_error e, file_name, file_contents print_error e, file_name, file_contents
@ -94,6 +95,17 @@ task 'watch', 'watch coffee/ for changes and build Chosen', ->
invoke 'build' invoke 'build'
)(file) )(file)
task 'package_npm', 'generate the package.json file for npm', package_npm = (cb) ->
try
package_file = 'package.json'
package = JSON.parse("#{fs.readFileSync package_file}")
package['version'] = version()
fs.writeFileSync package_file, JSON.stringify(package, null, 2)
console.log "Wrote #{package_file}"
cb() if typeof cb is 'function'
catch e
print_error e, package_file
run = (cmd, args, cb, err_cb) -> run = (cmd, args, cb, err_cb) ->
exec "#{cmd} #{args.join(' ')}", (err, stdout, stderr) -> exec "#{cmd} #{args.join(' ')}", (err, stdout, stderr) ->
if err isnt null if err isnt null

View File

@ -14,17 +14,20 @@ Contributions and pull requests are very welcome. Please follow these guidelines
1. Make all changes in Coffeescript files, **not** JavaScript files. 1. Make all changes in Coffeescript files, **not** JavaScript files.
2. For feature changes, update both jQuery *and* Prototype versions 2. For feature changes, update both jQuery *and* Prototype versions
3. Use 'cake build' to generate Chosen's JavaScript file and minified version. 3. Use `npm install -d` to install the correct development dependencies.
4. Don't touch the VERSION file 4. Use `cake build` or `cake watch` to generate Chosen's JavaScript file and minified version.
5. Submit a Pull Request using GitHub. 5. Don't touch the `VERSION` file
6. Submit a Pull Request using GitHub.
### Using CoffeeScript & Cake ### Using CoffeeScript & Cake
First, make sure you have the proper CoffeeScript / Cake set-up in place. First, make sure you have the proper CoffeeScript / Cake set-up in place. We have added a package.json that makes this easy:
1. Install Coffeescript: the [CoffeeScript documentation](http://jashkenas.github.com/coffee-script/) provides easy-to-follow instructions. ```
2. Install UglifyJS: <code>npm -g install uglify-js</code> npm install -d
3. Verify that your $NODE_PATH is properly configured using <code>echo $NODE_PATH</code> ```
This will install `coffee-script` and `uglifyjs`.
Once you're configured, building the JavasScript from the command line is easy: Once you're configured, building the JavasScript from the command line is easy:
@ -44,3 +47,4 @@ If you're interested, you can find the recipes in Cakefile.
- [Chosen for MooTools](https://github.com/julesjanssen/chosen), by Jules Janssen - [Chosen for MooTools](https://github.com/julesjanssen/chosen), by Jules Janssen
- [Chosen Drupal 7 Module](http://drupal.org/project/chosen), by Pol Dell'Aiera, Arshad Chummun, Bart Feenstra, Kálmán Hosszu, etc. - [Chosen Drupal 7 Module](http://drupal.org/project/chosen), by Pol Dell'Aiera, Arshad Chummun, Bart Feenstra, Kálmán Hosszu, etc.
- [Chosen CakePHP Plugin](https://github.com/paulredmond/chosen-cakephp), by Paul Redmond

View File

@ -1 +1 @@
0.9.5 0.9.7

Binary file not shown.

Before

Width:  |  Height:  |  Size: 396 B

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -23,29 +23,32 @@
/* @group Single Chosen */ /* @group Single Chosen */
.chzn-container-single .chzn-single { .chzn-container-single .chzn-single {
background-color: #fff; background-color: #ffffff;
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white)); filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0 );
background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 50%); background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #ffffff), color-stop(50%, #f6f6f6), color-stop(52%, #eeeeee), color-stop(100%, #f4f4f4));
background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%); background-image: -webkit-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
background-image: -o-linear-gradient(top, #eeeeee 0%,#ffffff 50%); background-image: -moz-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
background-image: -ms-linear-gradient(top, #eeeeee 0%,#ffffff 50%); background-image: -o-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 ); background-image: -ms-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
background-image: linear-gradient(top, #eeeeee 0%,#ffffff 50%); background-image: linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
-webkit-border-radius: 4px; -webkit-border-radius: 5px;
-moz-border-radius : 4px; -moz-border-radius : 5px;
border-radius : 4px; border-radius : 5px;
-moz-background-clip : padding; -moz-background-clip : padding;
-webkit-background-clip: padding-box; -webkit-background-clip: padding-box;
background-clip : padding-box; background-clip : padding-box;
border: 1px solid #aaa; border: 1px solid #aaaaaa;
-webkit-box-shadow: 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1);
-moz-box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1);
box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1);
display: block; display: block;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
position: relative; position: relative;
height: 26px; height: 23px;
line-height: 26px; line-height: 24px;
padding: 0 0 0 8px; padding: 0 0 0 8px;
color: #444; color: #444444;
text-decoration: none; text-decoration: none;
} }
.chzn-container-single .chzn-single span { .chzn-container-single .chzn-single span {
@ -61,7 +64,7 @@
display: block; display: block;
position: absolute; position: absolute;
right: 26px; right: 26px;
top: 8px; top: 6px;
width: 12px; width: 12px;
height: 13px; height: 13px;
font-size: 1px; font-size: 1px;
@ -71,21 +74,6 @@
background-position: right -11px; background-position: right -11px;
} }
.chzn-container-single .chzn-single div { .chzn-container-single .chzn-single div {
-webkit-border-radius: 0 4px 4px 0;
-moz-border-radius : 0 4px 4px 0;
border-radius : 0 4px 4px 0;
-moz-background-clip : padding;
-webkit-background-clip: padding-box;
background-clip : padding-box;
background: #ccc;
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee));
background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%);
background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%);
background-image: -o-linear-gradient(bottom, #ccc 0%, #eee 60%);
background-image: -ms-linear-gradient(top, #cccccc 0%,#eeeeee 60%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#cccccc', endColorstr='#eeeeee',GradientType=0 );
background-image: linear-gradient(top, #cccccc 0%,#eeeeee 60%);
border-left: 1px solid #aaa;
position: absolute; position: absolute;
right: 0; right: 0;
top: 0; top: 0;
@ -94,7 +82,7 @@
width: 18px; width: 18px;
} }
.chzn-container-single .chzn-single div b { .chzn-container-single .chzn-single div b {
background: url('chosen-sprite.png') no-repeat 0 1px; background: url('chosen-sprite.png') no-repeat 0 0;
display: block; display: block;
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -108,12 +96,12 @@
} }
.chzn-container-single .chzn-search input { .chzn-container-single .chzn-search input {
background: #fff url('chosen-sprite.png') no-repeat 100% -22px; background: #fff url('chosen-sprite.png') no-repeat 100% -22px;
background: url('chosen-sprite.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); background: url('chosen-sprite.png') no-repeat 100% -22px, -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
background: url('chosen-sprite.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%); background: url('chosen-sprite.png') no-repeat 100% -22px, -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background: url('chosen-sprite.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); background: url('chosen-sprite.png') no-repeat 100% -22px, -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background: url('chosen-sprite.png') no-repeat 100% -22px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%); background: url('chosen-sprite.png') no-repeat 100% -22px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background: url('chosen-sprite.png') no-repeat 100% -22px, -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%); background: url('chosen-sprite.png') no-repeat 100% -22px, -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background: url('chosen-sprite.png') no-repeat 100% -22px, linear-gradient(top, #ffffff 85%,#eeeeee 99%); background: url('chosen-sprite.png') no-repeat 100% -22px, linear-gradient(top, #eeeeee 1%, #ffffff 15%);
margin: 1px 0; margin: 1px 0;
padding: 4px 20px 4px 5px; padding: 4px 20px 4px 5px;
outline: 0; outline: 0;
@ -139,13 +127,12 @@
/* @group Multi Chosen */ /* @group Multi Chosen */
.chzn-container-multi .chzn-choices { .chzn-container-multi .chzn-choices {
background-color: #fff; background-color: #fff;
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
background-image: -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%); background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: -o-linear-gradient(bottom, white 85%, #eeeeee 99%); background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%); background-image: -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 ); background-image: linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: linear-gradient(top, #ffffff 85%,#eeeeee 99%);
border: 1px solid #aaa; border: 1px solid #aaa;
margin: 0; margin: 0;
padding: 0; padding: 0;
@ -168,6 +155,9 @@
color: #666; color: #666;
background: transparent !important; background: transparent !important;
border: 0 !important; border: 0 !important;
font-family: sans-serif;
font-size: 100%;
height: 15px;
padding: 5px; padding: 5px;
margin: 1px 0; margin: 1px 0;
outline: 0; outline: 0;
@ -187,21 +177,22 @@
-webkit-background-clip: padding-box; -webkit-background-clip: padding-box;
background-clip : padding-box; background-clip : padding-box;
background-color: #e4e4e4; background-color: #e4e4e4;
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #e4e4e4), color-stop(0.7, #eeeeee)); filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 );
background-image: -webkit-linear-gradient(center bottom, #e4e4e4 0%, #eeeeee 70%); background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
background-image: -moz-linear-gradient(center bottom, #e4e4e4 0%, #eeeeee 70%); background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
background-image: -o-linear-gradient(bottom, #e4e4e4 0%, #eeeeee 70%); background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
background-image: -ms-linear-gradient(top, #e4e4e4 0%,#eeeeee 70%); background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#e4e4e4', endColorstr='#eeeeee',GradientType=0 ); background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
background-image: linear-gradient(top, #e4e4e4 0%,#eeeeee 70%); background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
-webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
-moz-box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
color: #333; color: #333;
border: 1px solid #b4b4b4; border: 1px solid #aaaaaa;
line-height: 13px; line-height: 13px;
padding: 3px 19px 3px 6px; padding: 3px 20px 3px 5px;
margin: 3px 0 3px 5px; margin: 3px 0 3px 5px;
position: relative; position: relative;
}
.chzn-container-multi .chzn-choices .search-choice span {
cursor: default; cursor: default;
} }
.chzn-container-multi .chzn-choices .search-choice-focus { .chzn-container-multi .chzn-choices .search-choice-focus {
@ -228,7 +219,7 @@
/* @group Results */ /* @group Results */
.chzn-container .chzn-results { .chzn-container .chzn-results {
margin: 0 4px 4px 0; margin: 0 4px 4px 0;
max-height: 190px; max-height: 240px;
padding: 0 0 0 4px; padding: 0 0 0 4px;
position: relative; position: relative;
overflow-x: hidden; overflow-x: hidden;
@ -240,8 +231,8 @@
} }
.chzn-container .chzn-results li { .chzn-container .chzn-results li {
display: none; display: none;
line-height: 80%; line-height: 15px;
padding: 7px 7px 8px; padding: 5px 6px;
margin: 0; margin: 0;
list-style: none; list-style: none;
} }
@ -250,7 +241,14 @@
display: list-item; display: list-item;
} }
.chzn-container .chzn-results .highlighted { .chzn-container .chzn-results .highlighted {
background: #3875d7; background-color: #3875d7;
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#3875d7', endColorstr='#2a62bc', GradientType=0 );
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #3875d7), color-stop(90%, #2a62bc));
background-image: -webkit-linear-gradient(top, #3875d7 20%, #2a62bc 90%);
background-image: -moz-linear-gradient(top, #3875d7 20%, #2a62bc 90%);
background-image: -o-linear-gradient(top, #3875d7 20%, #2a62bc 90%);
background-image: -ms-linear-gradient(top, #3875d7 20%, #2a62bc 90%);
background-image: linear-gradient(top, #3875d7 20%, #2a62bc 90%);
color: #fff; color: #fff;
} }
.chzn-container .chzn-results li em { .chzn-container .chzn-results li em {
@ -270,11 +268,34 @@
font-weight: bold; font-weight: bold;
} }
.chzn-container .chzn-results .group-option { .chzn-container .chzn-results .group-option {
padding-left: 20px; padding-left: 15px;
} }
.chzn-container-multi .chzn-drop .result-selected { .chzn-container-multi .chzn-drop .result-selected {
display: none; display: none;
} }
.chzn-container .chzn-results-scroll {
background: white;
margin: 0 4px;
position: absolute;
text-align: center;
width: 321px; /* This should by dynamic with js */
z-index: 1;
}
.chzn-container .chzn-results-scroll span {
display: inline-block;
height: 17px;
text-indent: -5000px;
width: 9px;
}
.chzn-container .chzn-results-scroll-down {
bottom: 0;
}
.chzn-container .chzn-results-scroll-down span {
background: url('chosen-sprite.png') no-repeat -4px -3px;
}
.chzn-container .chzn-results-scroll-up span {
background: url('chosen-sprite.png') no-repeat -22px -3px;
}
/* @end */ /* @end */
/* @group Active */ /* @group Active */
@ -292,13 +313,13 @@
-o-box-shadow : 0 1px 0 #fff inset; -o-box-shadow : 0 1px 0 #fff inset;
box-shadow : 0 1px 0 #fff inset; box-shadow : 0 1px 0 #fff inset;
background-color: #eee; background-color: #eee;
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, white), color-stop(0.5, #eeeeee)); filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0 );
background-image: -webkit-linear-gradient(center bottom, white 0%, #eeeeee 50%); background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #eeeeee), color-stop(80%, #ffffff));
background-image: -moz-linear-gradient(center bottom, white 0%, #eeeeee 50%); background-image: -webkit-linear-gradient(top, #eeeeee 20%, #ffffff 80%);
background-image: -o-linear-gradient(bottom, white 0%, #eeeeee 50%); background-image: -moz-linear-gradient(top, #eeeeee 20%, #ffffff 80%);
background-image: -ms-linear-gradient(top, #ffffff 0%,#eeeeee 50%); background-image: -o-linear-gradient(top, #eeeeee 20%, #ffffff 80%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 ); background-image: -ms-linear-gradient(top, #eeeeee 20%, #ffffff 80%);
background-image: linear-gradient(top, #ffffff 0%,#eeeeee 50%); background-image: linear-gradient(top, #eeeeee 20%, #ffffff 80%);
-webkit-border-bottom-left-radius : 0; -webkit-border-bottom-left-radius : 0;
-webkit-border-bottom-right-radius: 0; -webkit-border-bottom-right-radius: 0;
-moz-border-radius-bottomleft : 0; -moz-border-radius-bottomleft : 0;
@ -338,34 +359,31 @@
} }
/* @group Right to Left */ /* @group Right to Left */
.chzn-rtl { direction:rtl;text-align: right; } .chzn-rtl { text-align: right; }
.chzn-rtl .chzn-single { padding-left: 0; padding-right: 8px; } .chzn-rtl .chzn-single { padding: 0 8px 0 0; overflow: visible; }
.chzn-rtl .chzn-single span { margin-left: 26px; margin-right: 0; } .chzn-rtl .chzn-single span { margin-left: 26px; margin-right: 0; direction: rtl; }
.chzn-rtl .chzn-single div {
left: 0; right: auto; .chzn-rtl .chzn-single div { left: 3px; right: auto; }
border-left: none; border-right: 1px solid #aaaaaa;
-webkit-border-radius: 4px 0 0 4px;
-moz-border-radius : 4px 0 0 4px;
border-radius : 4px 0 0 4px;
}
.chzn-rtl .chzn-single abbr { .chzn-rtl .chzn-single abbr {
left: 26px; left: 26px;
right: auto; right: auto;
} }
.chzn-rtl .chzn-choices .search-field input { direction: rtl; }
.chzn-rtl .chzn-choices li { float: right; } .chzn-rtl .chzn-choices li { float: right; }
.chzn-rtl .chzn-choices .search-choice { padding: 3px 6px 3px 19px; margin: 3px 5px 3px 0; } .chzn-rtl .chzn-choices .search-choice { padding: 3px 5px 3px 19px; margin: 3px 5px 3px 0; }
.chzn-rtl .chzn-choices .search-choice .search-choice-close { left: 5px; right: auto; background-position: right top;} .chzn-rtl .chzn-choices .search-choice .search-choice-close { left: 4px; right: auto; background-position: right top;}
.chzn-rtl.chzn-container-single .chzn-results { margin-left: 4px; margin-right: 0; padding-left: 0; padding-right: 4px; } .chzn-rtl.chzn-container-single .chzn-results { margin: 0 0 4px 4px; padding: 0 4px 0 0; }
.chzn-rtl .chzn-results .group-option { padding-left: 0; padding-right: 20px; } .chzn-rtl .chzn-results .group-option { padding-left: 0; padding-right: 15px; }
.chzn-rtl.chzn-container-active .chzn-single-with-drop div { border-right: none; } .chzn-rtl.chzn-container-active .chzn-single-with-drop div { border-right: none; }
.chzn-rtl .chzn-search input { .chzn-rtl .chzn-search input {
background: url('chosen-sprite.png') no-repeat -38px -22px, #ffffff; background: #fff url('chosen-sprite.png') no-repeat -38px -22px;
background: url('chosen-sprite.png') no-repeat -38px -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); background: url('chosen-sprite.png') no-repeat -38px -22px, -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
background: url('chosen-sprite.png') no-repeat -38px -22px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%); background: url('chosen-sprite.png') no-repeat -38px -22px, -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background: url('chosen-sprite.png') no-repeat -38px -22px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); background: url('chosen-sprite.png') no-repeat -38px -22px, -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background: url('chosen-sprite.png') no-repeat -38px -22px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%); background: url('chosen-sprite.png') no-repeat -38px -22px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background: url('chosen-sprite.png') no-repeat -38px -22px, -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%); background: url('chosen-sprite.png') no-repeat -38px -22px, -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background: url('chosen-sprite.png') no-repeat -38px -22px, linear-gradient(top, #ffffff 85%,#eeeeee 99%); background: url('chosen-sprite.png') no-repeat -38px -22px, linear-gradient(top, #eeeeee 1%, #ffffff 15%);
padding: 4px 5px 4px 20px; padding: 4px 5px 4px 20px;
direction: rtl;
} }
/* @end */ /* @end */

View File

@ -1,7 +1,7 @@
// Chosen, a Select Box Enhancer for jQuery and Protoype // Chosen, a Select Box Enhancer for jQuery and Protoype
// by Patrick Filler for Harvest, http://getharvest.com // by Patrick Filler for Harvest, http://getharvest.com
// //
// Version 0.9.5 // Version 0.9.7
// Full source at https://github.com/harvesthq/chosen // Full source at https://github.com/harvesthq/chosen
// Copyright (c) 2011 Harvest http://getharvest.com // Copyright (c) 2011 Harvest http://getharvest.com
@ -9,11 +9,14 @@
// This file is generated by `cake build`, do not edit it by hand. // This file is generated by `cake build`, do not edit it by hand.
(function() { (function() {
var SelectParser; var SelectParser;
SelectParser = (function() { SelectParser = (function() {
function SelectParser() { function SelectParser() {
this.options_index = 0; this.options_index = 0;
this.parsed = []; this.parsed = [];
} }
SelectParser.prototype.add_node = function(child) { SelectParser.prototype.add_node = function(child) {
if (child.nodeName === "OPTGROUP") { if (child.nodeName === "OPTGROUP") {
return this.add_group(child); return this.add_group(child);
@ -21,6 +24,7 @@
return this.add_option(child); return this.add_option(child);
} }
}; };
SelectParser.prototype.add_group = function(group) { SelectParser.prototype.add_group = function(group) {
var group_position, option, _i, _len, _ref, _results; var group_position, option, _i, _len, _ref, _results;
group_position = this.parsed.length; group_position = this.parsed.length;
@ -39,12 +43,11 @@
} }
return _results; return _results;
}; };
SelectParser.prototype.add_option = function(option, group_position, group_disabled) { SelectParser.prototype.add_option = function(option, group_position, group_disabled) {
if (option.nodeName === "OPTION") { if (option.nodeName === "OPTION") {
if (option.text !== "") { if (option.text !== "") {
if (group_position != null) { if (group_position != null) this.parsed[group_position].children += 1;
this.parsed[group_position].children += 1;
}
this.parsed.push({ this.parsed.push({
array_index: this.parsed.length, array_index: this.parsed.length,
options_index: this.options_index, options_index: this.options_index,
@ -67,8 +70,11 @@
return this.options_index += 1; return this.options_index += 1;
} }
}; };
return SelectParser; return SelectParser;
})(); })();
SelectParser.select_to_array = function(select) { SelectParser.select_to_array = function(select) {
var child, parser, _i, _len, _ref; var child, parser, _i, _len, _ref;
parser = new SelectParser(); parser = new SelectParser();
@ -79,17 +85,23 @@
} }
return parser.parsed; return parser.parsed;
}; };
this.SelectParser = SelectParser; this.SelectParser = SelectParser;
}).call(this); }).call(this);
/*
Chosen source: generate output using 'cake build'
Copyright (c) 2011 by Harvest
*/
(function() { (function() {
/*
Chosen source: generate output using 'cake build'
Copyright (c) 2011 by Harvest
*/
var AbstractChosen, root; var AbstractChosen, root;
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
root = this; root = this;
AbstractChosen = (function() { AbstractChosen = (function() {
function AbstractChosen(form_field, options) { function AbstractChosen(form_field, options) {
this.form_field = form_field; this.form_field = form_field;
this.options = options != null ? options : {}; this.options = options != null ? options : {};
@ -101,13 +113,15 @@
this.register_observers(); this.register_observers();
this.finish_setup(); this.finish_setup();
} }
AbstractChosen.prototype.set_default_values = function() { AbstractChosen.prototype.set_default_values = function() {
this.click_test_action = __bind(function(evt) { var _this = this;
return this.test_active_click(evt); this.click_test_action = function(evt) {
}, this); return _this.test_active_click(evt);
this.activate_action = __bind(function(evt) { };
return this.activate_field(evt); this.activate_action = function(evt) {
}, this); return _this.activate_field(evt);
};
this.active_field = false; this.active_field = false;
this.mouse_on_container = false; this.mouse_on_container = false;
this.results_showing = false; this.results_showing = false;
@ -118,52 +132,55 @@
this.choices = 0; this.choices = 0;
return this.results_none_found = this.options.no_results_text || "No results match"; return this.results_none_found = this.options.no_results_text || "No results match";
}; };
AbstractChosen.prototype.mouse_enter = function() { AbstractChosen.prototype.mouse_enter = function() {
return this.mouse_on_container = true; return this.mouse_on_container = true;
}; };
AbstractChosen.prototype.mouse_leave = function() { AbstractChosen.prototype.mouse_leave = function() {
return this.mouse_on_container = false; return this.mouse_on_container = false;
}; };
AbstractChosen.prototype.input_focus = function(evt) { AbstractChosen.prototype.input_focus = function(evt) {
var _this = this;
if (!this.active_field) { if (!this.active_field) {
return setTimeout((__bind(function() { return setTimeout((function() {
return this.container_mousedown(); return _this.container_mousedown();
}, this)), 50); }), 50);
} }
}; };
AbstractChosen.prototype.input_blur = function(evt) { AbstractChosen.prototype.input_blur = function(evt) {
var _this = this;
if (!this.mouse_on_container) { if (!this.mouse_on_container) {
this.active_field = false; this.active_field = false;
return setTimeout((__bind(function() { return setTimeout((function() {
return this.blur_test(); return _this.blur_test();
}, this)), 100); }), 100);
} }
}; };
AbstractChosen.prototype.result_add_option = function(option) { AbstractChosen.prototype.result_add_option = function(option) {
var classes, style; var classes, style;
if (!option.disabled) { if (!option.disabled) {
option.dom_id = this.container_id + "_o_" + option.array_index; option.dom_id = this.container_id + "_o_" + option.array_index;
classes = option.selected && this.is_multiple ? [] : ["active-result"]; classes = option.selected && this.is_multiple ? [] : ["active-result"];
if (option.selected) { if (option.selected) classes.push("result-selected");
classes.push("result-selected"); if (option.group_array_index != null) classes.push("group-option");
} if (option.classes !== "") classes.push(option.classes);
if (option.group_array_index != null) {
classes.push("group-option");
}
if (option.classes !== "") {
classes.push(option.classes);
}
style = option.style.cssText !== "" ? " style=\"" + option.style + "\"" : ""; style = option.style.cssText !== "" ? " style=\"" + option.style + "\"" : "";
return '<li id="' + option.dom_id + '" class="' + classes.join(' ') + '"' + style + '>' + option.html + '</li>'; return '<li id="' + option.dom_id + '" class="' + classes.join(' ') + '"' + style + '>' + option.html + '</li>';
} else { } else {
return ""; return "";
} }
}; };
AbstractChosen.prototype.results_update_field = function() { AbstractChosen.prototype.results_update_field = function() {
this.result_clear_highlight(); this.result_clear_highlight();
this.result_single_selected = null; this.result_single_selected = null;
return this.results_build(); return this.results_build();
}; };
AbstractChosen.prototype.results_toggle = function() { AbstractChosen.prototype.results_toggle = function() {
if (this.results_showing) { if (this.results_showing) {
return this.results_hide(); return this.results_hide();
@ -171,6 +188,7 @@
return this.results_show(); return this.results_show();
} }
}; };
AbstractChosen.prototype.results_search = function(evt) { AbstractChosen.prototype.results_search = function(evt) {
if (this.results_showing) { if (this.results_showing) {
return this.winnow_results(); return this.winnow_results();
@ -178,6 +196,7 @@
return this.results_show(); return this.results_show();
} }
}; };
AbstractChosen.prototype.keyup_checker = function(evt) { AbstractChosen.prototype.keyup_checker = function(evt) {
var stroke, _ref; var stroke, _ref;
stroke = (_ref = evt.which) != null ? _ref : evt.keyCode; stroke = (_ref = evt.which) != null ? _ref : evt.keyCode;
@ -193,15 +212,11 @@
break; break;
case 13: case 13:
evt.preventDefault(); evt.preventDefault();
if (this.results_showing) { if (this.results_showing) return this.result_select(evt);
return this.result_select(evt);
}
break; break;
case 27: case 27:
if (this.results_showing) { if (this.results_showing) this.results_hide();
return this.results_hide(); return true;
}
break;
case 9: case 9:
case 38: case 38:
case 40: case 40:
@ -213,62 +228,71 @@
return this.results_search(); return this.results_search();
} }
}; };
AbstractChosen.prototype.generate_field_id = function() { AbstractChosen.prototype.generate_field_id = function() {
var new_id; var new_id;
new_id = this.generate_random_id(); new_id = this.generate_random_id();
this.form_field.id = new_id; this.form_field.id = new_id;
return new_id; return new_id;
}; };
AbstractChosen.prototype.generate_random_char = function() { AbstractChosen.prototype.generate_random_char = function() {
var chars, newchar, rand; var chars, newchar, rand;
chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZ"; chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZ";
rand = Math.floor(Math.random() * chars.length); rand = Math.floor(Math.random() * chars.length);
return newchar = chars.substring(rand, rand + 1); return newchar = chars.substring(rand, rand + 1);
}; };
return AbstractChosen; return AbstractChosen;
})(); })();
root.AbstractChosen = AbstractChosen; root.AbstractChosen = AbstractChosen;
}).call(this); }).call(this);
/*
Chosen source: generate output using 'cake build'
Copyright (c) 2011 by Harvest
*/
(function() { (function() {
/* var $, Chosen, get_side_border_padding, root,
Chosen source: generate output using 'cake build' __hasProp = Object.prototype.hasOwnProperty,
Copyright (c) 2011 by Harvest __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
*/
var $, Chosen, get_side_border_padding, root;
var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
function ctor() { this.constructor = child; }
ctor.prototype = parent.prototype;
child.prototype = new ctor;
child.__super__ = parent.prototype;
return child;
}, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
root = this; root = this;
$ = jQuery; $ = jQuery;
$.fn.extend({ $.fn.extend({
chosen: function(options) { chosen: function(options) {
if ($.browser.msie && ($.browser.version === "6.0" || $.browser.version === "7.0")) { if ($.browser.msie && ($.browser.version === "6.0" || $.browser.version === "7.0")) {
return this; return this;
} }
return $(this).each(function(input_field) { return $(this).each(function(input_field) {
if (!($(this)).hasClass("chzn-done")) { if (!($(this)).hasClass("chzn-done")) return new Chosen(this, options);
return new Chosen(this, options);
}
}); });
} }
}); });
Chosen = (function() {
__extends(Chosen, AbstractChosen); Chosen = (function(_super) {
__extends(Chosen, _super);
function Chosen() { function Chosen() {
Chosen.__super__.constructor.apply(this, arguments); Chosen.__super__.constructor.apply(this, arguments);
} }
Chosen.prototype.setup = function() { Chosen.prototype.setup = function() {
this.form_field_jq = $(this.form_field); this.form_field_jq = $(this.form_field);
return this.is_rtl = this.form_field_jq.hasClass("chzn-rtl"); return this.is_rtl = this.form_field_jq.hasClass("chzn-rtl");
}; };
Chosen.prototype.finish_setup = function() { Chosen.prototype.finish_setup = function() {
return this.form_field_jq.addClass("chzn-done"); return this.form_field_jq.addClass("chzn-done");
}; };
Chosen.prototype.set_up_html = function() { Chosen.prototype.set_up_html = function() {
var container_div, dd_top, dd_width, sf_width; var container_div, dd_top, dd_width, sf_width;
this.container_id = this.form_field.id.length ? this.form_field.id.replace(/(:|\.)/g, '_') : this.generate_field_id(); this.container_id = this.form_field.id.length ? this.form_field.id.replace(/(:|\.)/g, '_') : this.generate_field_id();
@ -316,49 +340,56 @@
chosen: this chosen: this
}); });
}; };
Chosen.prototype.register_observers = function() { Chosen.prototype.register_observers = function() {
this.container.mousedown(__bind(function(evt) { var _this = this;
return this.container_mousedown(evt); this.container.mousedown(function(evt) {
}, this)); return _this.container_mousedown(evt);
this.container.mouseup(__bind(function(evt) { });
return this.container_mouseup(evt); this.container.mouseup(function(evt) {
}, this)); return _this.container_mouseup(evt);
this.container.mouseenter(__bind(function(evt) { });
return this.mouse_enter(evt); this.container.mouseenter(function(evt) {
}, this)); return _this.mouse_enter(evt);
this.container.mouseleave(__bind(function(evt) { });
return this.mouse_leave(evt); this.container.mouseleave(function(evt) {
}, this)); return _this.mouse_leave(evt);
this.search_results.mouseup(__bind(function(evt) { });
return this.search_results_mouseup(evt); this.search_results.mouseup(function(evt) {
}, this)); return _this.search_results_mouseup(evt);
this.search_results.mouseover(__bind(function(evt) { });
return this.search_results_mouseover(evt); this.search_results.mouseover(function(evt) {
}, this)); return _this.search_results_mouseover(evt);
this.search_results.mouseout(__bind(function(evt) { });
return this.search_results_mouseout(evt); this.search_results.mouseout(function(evt) {
}, this)); return _this.search_results_mouseout(evt);
this.form_field_jq.bind("liszt:updated", __bind(function(evt) { });
return this.results_update_field(evt); this.form_field_jq.bind("liszt:updated", function(evt) {
}, this)); return _this.results_update_field(evt);
this.search_field.blur(__bind(function(evt) { });
return this.input_blur(evt); this.search_field.blur(function(evt) {
}, this)); return _this.input_blur(evt);
this.search_field.keyup(__bind(function(evt) { });
return this.keyup_checker(evt); this.search_field.keyup(function(evt) {
}, this)); return _this.keyup_checker(evt);
this.search_field.keydown(__bind(function(evt) { });
return this.keydown_checker(evt); this.search_field.keydown(function(evt) {
}, this)); return _this.keydown_checker(evt);
});
if (this.is_multiple) { if (this.is_multiple) {
this.search_choices.click(__bind(function(evt) { this.search_choices.click(function(evt) {
return this.choices_click(evt); return _this.choices_click(evt);
}, this)); });
return this.search_field.focus(__bind(function(evt) { return this.search_field.focus(function(evt) {
return this.input_focus(evt); return _this.input_focus(evt);
}, this)); });
} else {
return this.container.click(function(evt) {
return evt.preventDefault();
});
} }
}; };
Chosen.prototype.search_field_disabled = function() { Chosen.prototype.search_field_disabled = function() {
this.is_disabled = this.form_field_jq[0].disabled; this.is_disabled = this.form_field_jq[0].disabled;
if (this.is_disabled) { if (this.is_disabled) {
@ -376,18 +407,15 @@
} }
} }
}; };
Chosen.prototype.container_mousedown = function(evt) { Chosen.prototype.container_mousedown = function(evt) {
var target_closelink; var target_closelink;
if (!this.is_disabled) { if (!this.is_disabled) {
target_closelink = evt != null ? ($(evt.target)).hasClass("search-choice-close") : false; target_closelink = evt != null ? ($(evt.target)).hasClass("search-choice-close") : false;
if (evt && evt.type === "mousedown") { if (evt && evt.type === "mousedown") evt.stopPropagation();
evt.stopPropagation();
}
if (!this.pending_destroy_click && !target_closelink) { if (!this.pending_destroy_click && !target_closelink) {
if (!this.active_field) { if (!this.active_field) {
if (this.is_multiple) { if (this.is_multiple) this.search_field.val("");
this.search_field.val("");
}
$(document).click(this.click_test_action); $(document).click(this.click_test_action);
this.results_show(); this.results_show();
} else if (!this.is_multiple && evt && (($(evt.target)[0] === this.selected_item[0]) || $(evt.target).parents("a.chzn-single").length)) { } else if (!this.is_multiple && evt && (($(evt.target)[0] === this.selected_item[0]) || $(evt.target).parents("a.chzn-single").length)) {
@ -400,16 +428,17 @@
} }
} }
}; };
Chosen.prototype.container_mouseup = function(evt) { Chosen.prototype.container_mouseup = function(evt) {
if (evt.target.nodeName === "ABBR") { if (evt.target.nodeName === "ABBR") return this.results_reset(evt);
return this.results_reset(evt);
}
}; };
Chosen.prototype.blur_test = function(evt) { Chosen.prototype.blur_test = function(evt) {
if (!this.active_field && this.container.hasClass("chzn-container-active")) { if (!this.active_field && this.container.hasClass("chzn-container-active")) {
return this.close_field(); return this.close_field();
} }
}; };
Chosen.prototype.close_field = function() { Chosen.prototype.close_field = function() {
$(document).unbind("click", this.click_test_action); $(document).unbind("click", this.click_test_action);
if (!this.is_multiple) { if (!this.is_multiple) {
@ -424,6 +453,7 @@
this.show_search_field_default(); this.show_search_field_default();
return this.search_field_scale(); return this.search_field_scale();
}; };
Chosen.prototype.activate_field = function() { Chosen.prototype.activate_field = function() {
if (!this.is_multiple && !this.active_field) { if (!this.is_multiple && !this.active_field) {
this.search_field.attr("tabindex", this.selected_item.attr("tabindex")); this.search_field.attr("tabindex", this.selected_item.attr("tabindex"));
@ -434,6 +464,7 @@
this.search_field.val(this.search_field.val()); this.search_field.val(this.search_field.val());
return this.search_field.focus(); return this.search_field.focus();
}; };
Chosen.prototype.test_active_click = function(evt) { Chosen.prototype.test_active_click = function(evt) {
if ($(evt.target).parents('#' + this.container_id).length) { if ($(evt.target).parents('#' + this.container_id).length) {
return this.active_field = true; return this.active_field = true;
@ -441,6 +472,7 @@
return this.close_field(); return this.close_field();
} }
}; };
Chosen.prototype.results_build = function() { Chosen.prototype.results_build = function() {
var content, data, _i, _len, _ref; var content, data, _i, _len, _ref;
this.parsing = true; this.parsing = true;
@ -468,9 +500,7 @@
this.choice_build(data); this.choice_build(data);
} else if (data.selected && !this.is_multiple) { } else if (data.selected && !this.is_multiple) {
this.selected_item.find("span").text(data.text); this.selected_item.find("span").text(data.text);
if (this.allow_single_deselect) { if (this.allow_single_deselect) this.single_deselect_control_build();
this.single_deselect_control_build();
}
} }
} }
} }
@ -480,6 +510,7 @@
this.search_results.html(content); this.search_results.html(content);
return this.parsing = false; return this.parsing = false;
}; };
Chosen.prototype.result_add_group = function(group) { Chosen.prototype.result_add_group = function(group) {
if (!group.disabled) { if (!group.disabled) {
group.dom_id = this.container_id + "_g_" + group.array_index; group.dom_id = this.container_id + "_g_" + group.array_index;
@ -488,6 +519,7 @@
return ""; return "";
} }
}; };
Chosen.prototype.result_do_highlight = function(el) { Chosen.prototype.result_do_highlight = function(el) {
var high_bottom, high_top, maxHeight, visible_bottom, visible_top; var high_bottom, high_top, maxHeight, visible_bottom, visible_top;
if (el.length) { if (el.length) {
@ -506,12 +538,12 @@
} }
} }
}; };
Chosen.prototype.result_clear_highlight = function() { Chosen.prototype.result_clear_highlight = function() {
if (this.result_highlight) { if (this.result_highlight) this.result_highlight.removeClass("highlighted");
this.result_highlight.removeClass("highlighted");
}
return this.result_highlight = null; return this.result_highlight = null;
}; };
Chosen.prototype.results_show = function() { Chosen.prototype.results_show = function() {
var dd_top; var dd_top;
if (!this.is_multiple) { if (!this.is_multiple) {
@ -530,6 +562,7 @@
this.search_field.val(this.search_field.val()); this.search_field.val(this.search_field.val());
return this.winnow_results(); return this.winnow_results();
}; };
Chosen.prototype.results_hide = function() { Chosen.prototype.results_hide = function() {
if (!this.is_multiple) { if (!this.is_multiple) {
this.selected_item.removeClass("chzn-single-with-drop"); this.selected_item.removeClass("chzn-single-with-drop");
@ -540,6 +573,7 @@
}); });
return this.results_showing = false; return this.results_showing = false;
}; };
Chosen.prototype.set_tab_index = function(el) { Chosen.prototype.set_tab_index = function(el) {
var ti; var ti;
if (this.form_field_jq.attr("tabindex")) { if (this.form_field_jq.attr("tabindex")) {
@ -553,6 +587,7 @@
} }
} }
}; };
Chosen.prototype.show_search_field_default = function() { Chosen.prototype.show_search_field_default = function() {
if (this.is_multiple && this.choices < 1 && !this.active_field) { if (this.is_multiple && this.choices < 1 && !this.active_field) {
this.search_field.val(this.default_text); this.search_field.val(this.default_text);
@ -562,6 +597,7 @@
return this.search_field.removeClass("default"); return this.search_field.removeClass("default");
} }
}; };
Chosen.prototype.search_results_mouseup = function(evt) { Chosen.prototype.search_results_mouseup = function(evt) {
var target; var target;
target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first(); target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
@ -570,34 +606,38 @@
return this.result_select(evt); return this.result_select(evt);
} }
}; };
Chosen.prototype.search_results_mouseover = function(evt) { Chosen.prototype.search_results_mouseover = function(evt) {
var target; var target;
target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first(); target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
if (target) { if (target) return this.result_do_highlight(target);
return this.result_do_highlight(target);
}
}; };
Chosen.prototype.search_results_mouseout = function(evt) { Chosen.prototype.search_results_mouseout = function(evt) {
if ($(evt.target).hasClass("active-result" || $(evt.target).parents('.active-result').first())) { if ($(evt.target).hasClass("active-result" || $(evt.target).parents('.active-result').first())) {
return this.result_clear_highlight(); return this.result_clear_highlight();
} }
}; };
Chosen.prototype.choices_click = function(evt) { Chosen.prototype.choices_click = function(evt) {
evt.preventDefault(); evt.preventDefault();
if (this.active_field && !($(evt.target).hasClass("search-choice" || $(evt.target).parents('.search-choice').first)) && !this.results_showing) { if (this.active_field && !($(evt.target).hasClass("search-choice" || $(evt.target).parents('.search-choice').first)) && !this.results_showing) {
return this.results_show(); return this.results_show();
} }
}; };
Chosen.prototype.choice_build = function(item) { Chosen.prototype.choice_build = function(item) {
var choice_id, link; var choice_id, link,
_this = this;
choice_id = this.container_id + "_c_" + item.array_index; choice_id = this.container_id + "_c_" + item.array_index;
this.choices += 1; this.choices += 1;
this.search_container.before('<li class="search-choice" id="' + choice_id + '"><span>' + item.html + '</span><a href="javascript:void(0)" class="search-choice-close" rel="' + item.array_index + '"></a></li>'); this.search_container.before('<li class="search-choice" id="' + choice_id + '"><span>' + item.html + '</span><a href="javascript:void(0)" class="search-choice-close" rel="' + item.array_index + '"></a></li>');
link = $('#' + choice_id).find("a").first(); link = $('#' + choice_id).find("a").first();
return link.click(__bind(function(evt) { return link.click(function(evt) {
return this.choice_destroy_link_click(evt); return _this.choice_destroy_link_click(evt);
}, this)); });
}; };
Chosen.prototype.choice_destroy_link_click = function(evt) { Chosen.prototype.choice_destroy_link_click = function(evt) {
evt.preventDefault(); evt.preventDefault();
if (!this.is_disabled) { if (!this.is_disabled) {
@ -607,6 +647,7 @@
return evt.stopPropagation; return evt.stopPropagation;
} }
}; };
Chosen.prototype.choice_destroy = function(link) { Chosen.prototype.choice_destroy = function(link) {
this.choices -= 1; this.choices -= 1;
this.show_search_field_default(); this.show_search_field_default();
@ -616,16 +657,16 @@
this.result_deselect(link.attr("rel")); this.result_deselect(link.attr("rel"));
return link.parents('li').first().remove(); return link.parents('li').first().remove();
}; };
Chosen.prototype.results_reset = function(evt) { Chosen.prototype.results_reset = function(evt) {
this.form_field.options[0].selected = true; this.form_field.options[0].selected = true;
this.selected_item.find("span").text(this.default_text); this.selected_item.find("span").text(this.default_text);
this.show_search_field_default(); this.show_search_field_default();
$(evt.target).remove(); $(evt.target).remove();
this.form_field_jq.trigger("change"); this.form_field_jq.trigger("change");
if (this.active_field) { if (this.active_field) return this.results_hide();
return this.results_hide();
}
}; };
Chosen.prototype.result_select = function(evt) { Chosen.prototype.result_select = function(evt) {
var high, high_id, item, position; var high, high_id, item, position;
if (this.result_highlight) { if (this.result_highlight) {
@ -647,24 +688,23 @@
this.choice_build(item); this.choice_build(item);
} else { } else {
this.selected_item.find("span").first().text(item.text); this.selected_item.find("span").first().text(item.text);
if (this.allow_single_deselect) { if (this.allow_single_deselect) this.single_deselect_control_build();
this.single_deselect_control_build();
}
}
if (!(evt.metaKey && this.is_multiple)) {
this.results_hide();
} }
if (!(evt.metaKey && this.is_multiple)) this.results_hide();
this.search_field.val(""); this.search_field.val("");
this.form_field_jq.trigger("change"); this.form_field_jq.trigger("change");
return this.search_field_scale(); return this.search_field_scale();
} }
}; };
Chosen.prototype.result_activate = function(el) { Chosen.prototype.result_activate = function(el) {
return el.addClass("active-result"); return el.addClass("active-result");
}; };
Chosen.prototype.result_deactivate = function(el) { Chosen.prototype.result_deactivate = function(el) {
return el.removeClass("active-result"); return el.removeClass("active-result");
}; };
Chosen.prototype.result_deselect = function(pos) { Chosen.prototype.result_deselect = function(pos) {
var result, result_data; var result, result_data;
result_data = this.results_data[pos]; result_data = this.results_data[pos];
@ -677,11 +717,13 @@
this.form_field_jq.trigger("change"); this.form_field_jq.trigger("change");
return this.search_field_scale(); return this.search_field_scale();
}; };
Chosen.prototype.single_deselect_control_build = function() { Chosen.prototype.single_deselect_control_build = function() {
if (this.allow_single_deselect && this.selected_item.find("abbr").length < 1) { if (this.allow_single_deselect && this.selected_item.find("abbr").length < 1) {
return this.selected_item.find("span").first().after("<abbr class=\"search-choice-close\"></abbr>"); return this.selected_item.find("span").first().after("<abbr class=\"search-choice-close\"></abbr>");
} }
}; };
Chosen.prototype.winnow_results = function() { Chosen.prototype.winnow_results = function() {
var found, option, part, parts, regex, result, result_id, results, searchText, startpos, text, zregex, _i, _j, _len, _len2, _ref; var found, option, part, parts, regex, result, result_id, results, searchText, startpos, text, zregex, _i, _j, _len, _len2, _ref;
this.no_results_clear(); this.no_results_clear();
@ -742,6 +784,7 @@
return this.winnow_results_set_highlight(); return this.winnow_results_set_highlight();
} }
}; };
Chosen.prototype.winnow_results_clear = function() { Chosen.prototype.winnow_results_clear = function() {
var li, lis, _i, _len, _results; var li, lis, _i, _len, _results;
this.search_field.val(""); this.search_field.val("");
@ -750,46 +793,49 @@
for (_i = 0, _len = lis.length; _i < _len; _i++) { for (_i = 0, _len = lis.length; _i < _len; _i++) {
li = lis[_i]; li = lis[_i];
li = $(li); li = $(li);
_results.push(li.hasClass("group-result") ? li.css('display', 'auto') : !this.is_multiple || !li.hasClass("result-selected") ? this.result_activate(li) : void 0); if (li.hasClass("group-result")) {
_results.push(li.css('display', 'auto'));
} else if (!this.is_multiple || !li.hasClass("result-selected")) {
_results.push(this.result_activate(li));
} else {
_results.push(void 0);
}
} }
return _results; return _results;
}; };
Chosen.prototype.winnow_results_set_highlight = function() { Chosen.prototype.winnow_results_set_highlight = function() {
var do_high, selected_results; var do_high, selected_results;
if (!this.result_highlight) { if (!this.result_highlight) {
selected_results = !this.is_multiple ? this.search_results.find(".result-selected.active-result") : []; selected_results = !this.is_multiple ? this.search_results.find(".result-selected.active-result") : [];
do_high = selected_results.length ? selected_results.first() : this.search_results.find(".active-result").first(); do_high = selected_results.length ? selected_results.first() : this.search_results.find(".active-result").first();
if (do_high != null) { if (do_high != null) return this.result_do_highlight(do_high);
return this.result_do_highlight(do_high);
}
} }
}; };
Chosen.prototype.no_results = function(terms) { Chosen.prototype.no_results = function(terms) {
var no_results_html; var no_results_html;
no_results_html = $('<li class="no-results">' + this.results_none_found + ' "<span></span>"</li>'); no_results_html = $('<li class="no-results">' + this.results_none_found + ' "<span></span>"</li>');
no_results_html.find("span").first().html(terms); no_results_html.find("span").first().html(terms);
return this.search_results.append(no_results_html); return this.search_results.append(no_results_html);
}; };
Chosen.prototype.no_results_clear = function() { Chosen.prototype.no_results_clear = function() {
return this.search_results.find(".no-results").remove(); return this.search_results.find(".no-results").remove();
}; };
Chosen.prototype.keydown_arrow = function() { Chosen.prototype.keydown_arrow = function() {
var first_active, next_sib; var first_active, next_sib;
if (!this.result_highlight) { if (!this.result_highlight) {
first_active = this.search_results.find("li.active-result").first(); first_active = this.search_results.find("li.active-result").first();
if (first_active) { if (first_active) this.result_do_highlight($(first_active));
this.result_do_highlight($(first_active));
}
} else if (this.results_showing) { } else if (this.results_showing) {
next_sib = this.result_highlight.nextAll("li.active-result").first(); next_sib = this.result_highlight.nextAll("li.active-result").first();
if (next_sib) { if (next_sib) this.result_do_highlight(next_sib);
this.result_do_highlight(next_sib);
}
}
if (!this.results_showing) {
return this.results_show();
} }
if (!this.results_showing) return this.results_show();
}; };
Chosen.prototype.keyup_arrow = function() { Chosen.prototype.keyup_arrow = function() {
var prev_sibs; var prev_sibs;
if (!this.results_showing && !this.is_multiple) { if (!this.results_showing && !this.is_multiple) {
@ -799,13 +845,12 @@
if (prev_sibs.length) { if (prev_sibs.length) {
return this.result_do_highlight(prev_sibs.first()); return this.result_do_highlight(prev_sibs.first());
} else { } else {
if (this.choices > 0) { if (this.choices > 0) this.results_hide();
this.results_hide();
}
return this.result_clear_highlight(); return this.result_clear_highlight();
} }
} }
}; };
Chosen.prototype.keydown_backstroke = function() { Chosen.prototype.keydown_backstroke = function() {
if (this.pending_backstroke) { if (this.pending_backstroke) {
this.choice_destroy(this.pending_backstroke.find("a").first()); this.choice_destroy(this.pending_backstroke.find("a").first());
@ -815,27 +860,25 @@
return this.pending_backstroke.addClass("search-choice-focus"); return this.pending_backstroke.addClass("search-choice-focus");
} }
}; };
Chosen.prototype.clear_backstroke = function() { Chosen.prototype.clear_backstroke = function() {
if (this.pending_backstroke) { if (this.pending_backstroke) {
this.pending_backstroke.removeClass("search-choice-focus"); this.pending_backstroke.removeClass("search-choice-focus");
} }
return this.pending_backstroke = null; return this.pending_backstroke = null;
}; };
Chosen.prototype.keydown_checker = function(evt) { Chosen.prototype.keydown_checker = function(evt) {
var stroke, _ref; var stroke, _ref;
stroke = (_ref = evt.which) != null ? _ref : evt.keyCode; stroke = (_ref = evt.which) != null ? _ref : evt.keyCode;
this.search_field_scale(); this.search_field_scale();
if (stroke !== 8 && this.pending_backstroke) { if (stroke !== 8 && this.pending_backstroke) this.clear_backstroke();
this.clear_backstroke();
}
switch (stroke) { switch (stroke) {
case 8: case 8:
this.backstroke_length = this.search_field.val().length; this.backstroke_length = this.search_field.val().length;
break; break;
case 9: case 9:
if (this.results_showing && !this.is_multiple) { if (this.results_showing && !this.is_multiple) this.result_select(evt);
this.result_select(evt);
}
this.mouse_on_container = false; this.mouse_on_container = false;
break; break;
case 13: case 13:
@ -850,6 +893,7 @@
break; break;
} }
}; };
Chosen.prototype.search_field_scale = function() { Chosen.prototype.search_field_scale = function() {
var dd_top, div, h, style, style_block, styles, w, _i, _len; var dd_top, div, h, style, style_block, styles, w, _i, _len;
if (this.is_multiple) { if (this.is_multiple) {
@ -868,9 +912,7 @@
$('body').append(div); $('body').append(div);
w = div.width() + 25; w = div.width() + 25;
div.remove(); div.remove();
if (w > this.f_width - 10) { if (w > this.f_width - 10) w = this.f_width - 10;
w = this.f_width - 10;
}
this.search_field.css({ this.search_field.css({
'width': w + 'px' 'width': w + 'px'
}); });
@ -880,6 +922,7 @@
}); });
} }
}; };
Chosen.prototype.generate_random_id = function() { Chosen.prototype.generate_random_id = function() {
var string; var string;
string = "sel" + this.generate_random_char() + this.generate_random_char() + this.generate_random_char(); string = "sel" + this.generate_random_char() + this.generate_random_char() + this.generate_random_char();
@ -888,11 +931,16 @@
} }
return string; return string;
}; };
return Chosen; return Chosen;
})();
})(AbstractChosen);
get_side_border_padding = function(elmt) { get_side_border_padding = function(elmt) {
var side_border_padding; var side_border_padding;
return side_border_padding = elmt.outerWidth() - elmt.width(); return side_border_padding = elmt.outerWidth() - elmt.width();
}; };
root.get_side_border_padding = get_side_border_padding; root.get_side_border_padding = get_side_border_padding;
}).call(this); }).call(this);

View File

@ -1,10 +1,10 @@
// Chosen, a Select Box Enhancer for jQuery and Protoype // Chosen, a Select Box Enhancer for jQuery and Protoype
// by Patrick Filler for Harvest, http://getharvest.com // by Patrick Filler for Harvest, http://getharvest.com
// //
// Version 0.9.5 // Version 0.9.7
// Full source at https://github.com/harvesthq/chosen // Full source at https://github.com/harvesthq/chosen
// Copyright (c) 2011 Harvest http://getharvest.com // Copyright (c) 2011 Harvest http://getharvest.com
// MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md // MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
// This file is generated by `cake build`, do not edit it by hand. // This file is generated by `cake build`, do not edit it by hand.
(function(){var a;a=function(){function a(){this.options_index=0,this.parsed=[]}return a.prototype.add_node=function(a){return a.nodeName==="OPTGROUP"?this.add_group(a):this.add_option(a)},a.prototype.add_group=function(a){var b,c,d,e,f,g;b=this.parsed.length,this.parsed.push({array_index:b,group:!0,label:a.label,children:0,disabled:a.disabled}),f=a.childNodes,g=[];for(d=0,e=f.length;d<e;d++)c=f[d],g.push(this.add_option(c,b,a.disabled));return g},a.prototype.add_option=function(a,b,c){if(a.nodeName==="OPTION")return a.text!==""?(b!=null&&(this.parsed[b].children+=1),this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:a.value,text:a.text,html:a.innerHTML,selected:a.selected,disabled:c===!0?c:a.disabled,group_array_index:b,classes:a.className,style:a.style.cssText})):this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:!0}),this.options_index+=1},a}(),a.select_to_array=function(b){var c,d,e,f,g;d=new a,g=b.childNodes;for(e=0,f=g.length;e<f;e++)c=g[e],d.add_node(c);return d.parsed},this.SelectParser=a}).call(this),function(){var a,b,c=function(a,b){return function(){return a.apply(b,arguments)}};b=this,a=function(){function a(a,b){this.form_field=a,this.options=b!=null?b:{},this.set_default_values(),this.is_multiple=this.form_field.multiple,this.default_text_default=this.is_multiple?"Select Some Options":"Select an Option",this.setup(),this.set_up_html(),this.register_observers(),this.finish_setup()}return a.prototype.set_default_values=function(){return this.click_test_action=c(function(a){return this.test_active_click(a)},this),this.activate_action=c(function(a){return this.activate_field(a)},this),this.active_field=!1,this.mouse_on_container=!1,this.results_showing=!1,this.result_highlighted=null,this.result_single_selected=null,this.allow_single_deselect=this.options.allow_single_deselect!=null&&this.form_field.options[0]!=null&&this.form_field.options[0].text===""?this.options.allow_single_deselect:!1,this.disable_search_threshold=this.options.disable_search_threshold||0,this.choices=0,this.results_none_found=this.options.no_results_text||"No results match"},a.prototype.mouse_enter=function(){return this.mouse_on_container=!0},a.prototype.mouse_leave=function(){return this.mouse_on_container=!1},a.prototype.input_focus=function(a){if(!this.active_field)return setTimeout(c(function(){return this.container_mousedown()},this),50)},a.prototype.input_blur=function(a){if(!this.mouse_on_container)return this.active_field=!1,setTimeout(c(function(){return this.blur_test()},this),100)},a.prototype.result_add_option=function(a){var b,c;return a.disabled?"":(a.dom_id=this.container_id+"_o_"+a.array_index,b=a.selected&&this.is_multiple?[]:["active-result"],a.selected&&b.push("result-selected"),a.group_array_index!=null&&b.push("group-option"),a.classes!==""&&b.push(a.classes),c=a.style.cssText!==""?' style="'+a.style+'"':"",'<li id="'+a.dom_id+'" class="'+b.join(" ")+'"'+c+">"+a.html+"</li>")},a.prototype.results_update_field=function(){return this.result_clear_highlight(),this.result_single_selected=null,this.results_build()},a.prototype.results_toggle=function(){return this.results_showing?this.results_hide():this.results_show()},a.prototype.results_search=function(a){return this.results_showing?this.winnow_results():this.results_show()},a.prototype.keyup_checker=function(a){var b,c;b=(c=a.which)!=null?c:a.keyCode,this.search_field_scale();switch(b){case 8:if(this.is_multiple&&this.backstroke_length<1&&this.choices>0)return this.keydown_backstroke();if(!this.pending_backstroke)return this.result_clear_highlight(),this.results_search();break;case 13:a.preventDefault();if(this.results_showing)return this.result_select(a);break;case 27:if(this.results_showing)return this.results_hide();break;case 9:case 38:case 40:case 16:case 91:case 17:break;default:return this.results_search()}},a.prototype.generate_field_id=function(){var a;return a=this.generate_random_id(),this.form_field.id=a,a},a.prototype.generate_random_char=function(){var a,b,c;return a="0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZ",c=Math.floor(Math.random()*a.length),b=a.substring(c,c+1)},a}(),b.AbstractChosen=a}.call(this),function(){var a,b,c,d,e=Object.prototype.hasOwnProperty,f=function(a,b){function d(){this.constructor=a}for(var c in b)e.call(b,c)&&(a[c]=b[c]);return d.prototype=b.prototype,a.prototype=new d,a.__super__=b.prototype,a},g=function(a,b){return function(){return a.apply(b,arguments)}};d=this,a=jQuery,a.fn.extend({chosen:function(c){return!a.browser.msie||a.browser.version!=="6.0"&&a.browser.version!=="7.0"?a(this).each(function(d){if(!a(this).hasClass("chzn-done"))return new b(this,c)}):this}}),b=function(){function b(){b.__super__.constructor.apply(this,arguments)}return f(b,AbstractChosen),b.prototype.setup=function(){return this.form_field_jq=a(this.form_field),this.is_rtl=this.form_field_jq.hasClass("chzn-rtl")},b.prototype.finish_setup=function(){return this.form_field_jq.addClass("chzn-done")},b.prototype.set_up_html=function(){var b,d,e,f;return this.container_id=this.form_field.id.length?this.form_field.id.replace(/(:|\.)/g,"_"):this.generate_field_id(),this.container_id+="_chzn",this.f_width=this.form_field_jq.outerWidth(),this.default_text=this.form_field_jq.data("placeholder")?this.form_field_jq.data("placeholder"):this.default_text_default,b=a("<div />",{id:this.container_id,"class":"chzn-container"+(this.is_rtl?" chzn-rtl":""),style:"width: "+this.f_width+"px;"}),this.is_multiple?b.html('<ul class="chzn-choices"><li class="search-field"><input type="text" value="'+this.default_text+'" class="default" autocomplete="off" style="width:25px;" /></li></ul><div class="chzn-drop" style="left:-9000px;"><ul class="chzn-results"></ul></div>'):b.html('<a href="javascript:void(0)" class="chzn-single"><span>'+this.default_text+'</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" autocomplete="off" /></div><ul class="chzn-results"></ul></div>'),this.form_field_jq.hide().after(b),this.container=a("#"+this.container_id),this.container.addClass("chzn-container-"+(this.is_multiple?"multi":"single")),this.dropdown=this.container.find("div.chzn-drop").first(),d=this.container.height(),e=this.f_width-c(this.dropdown),this.dropdown.css({width:e+"px",top:d+"px"}),this.search_field=this.container.find("input").first(),this.search_results=this.container.find("ul.chzn-results").first(),this.search_field_scale(),this.search_no_results=this.container.find("li.no-results").first(),this.is_multiple?(this.search_choices=this.container.find("ul.chzn-choices").first(),this.search_container=this.container.find("li.search-field").first()):(this.search_container=this.container.find("div.chzn-search").first(),this.selected_item=this.container.find(".chzn-single").first(),f=e-c(this.search_container)-c(this.search_field),this.search_field.css({width:f+"px"})),this.results_build(),this.set_tab_index(),this.form_field_jq.trigger("liszt:ready",{chosen:this})},b.prototype.register_observers=function(){this.container.mousedown(g(function(a){return this.container_mousedown(a)},this)),this.container.mouseup(g(function(a){return this.container_mouseup(a)},this)),this.container.mouseenter(g(function(a){return this.mouse_enter(a)},this)),this.container.mouseleave(g(function(a){return this.mouse_leave(a)},this)),this.search_results.mouseup(g(function(a){return this.search_results_mouseup(a)},this)),this.search_results.mouseover(g(function(a){return this.search_results_mouseover(a)},this)),this.search_results.mouseout(g(function(a){return this.search_results_mouseout(a)},this)),this.form_field_jq.bind("liszt:updated",g(function(a){return this.results_update_field(a)},this)),this.search_field.blur(g(function(a){return this.input_blur(a)},this)),this.search_field.keyup(g(function(a){return this.keyup_checker(a)},this)),this.search_field.keydown(g(function(a){return this.keydown_checker(a)},this));if(this.is_multiple)return this.search_choices.click(g(function(a){return this.choices_click(a)},this)),this.search_field.focus(g(function(a){return this.input_focus(a)},this))},b.prototype.search_field_disabled=function(){this.is_disabled=this.form_field_jq[0].disabled;if(this.is_disabled)return this.container.addClass("chzn-disabled"),this.search_field[0].disabled=!0,this.is_multiple||this.selected_item.unbind("focus",this.activate_action),this.close_field();this.container.removeClass("chzn-disabled"),this.search_field[0].disabled=!1;if(!this.is_multiple)return this.selected_item.bind("focus",this.activate_action)},b.prototype.container_mousedown=function(b){var c;if(!this.is_disabled)return c=b!=null?a(b.target).hasClass("search-choice-close"):!1,b&&b.type==="mousedown"&&b.stopPropagation(),!this.pending_destroy_click&&!c?(this.active_field?!this.is_multiple&&b&&(a(b.target)[0]===this.selected_item[0]||a(b.target).parents("a.chzn-single").length)&&(b.preventDefault(),this.results_toggle()):(this.is_multiple&&this.search_field.val(""),a(document).click(this.click_test_action),this.results_show()),this.activate_field()):this.pending_destroy_click=!1},b.prototype.container_mouseup=function(a){if(a.target.nodeName==="ABBR")return this.results_reset(a)},b.prototype.blur_test=function(a){if(!this.active_field&&this.container.hasClass("chzn-container-active"))return this.close_field()},b.prototype.close_field=function(){return a(document).unbind("click",this.click_test_action),this.is_multiple||(this.selected_item.attr("tabindex",this.search_field.attr("tabindex")),this.search_field.attr("tabindex",-1)),this.active_field=!1,this.results_hide(),this.container.removeClass("chzn-container-active"),this.winnow_results_clear(),this.clear_backstroke(),this.show_search_field_default(),this.search_field_scale()},b.prototype.activate_field=function(){return!this.is_multiple&&!this.active_field&&(this.search_field.attr("tabindex",this.selected_item.attr("tabindex")),this.selected_item.attr("tabindex",-1)),this.container.addClass("chzn-container-active"),this.active_field=!0,this.search_field.val(this.search_field.val()),this.search_field.focus()},b.prototype.test_active_click=function(b){return a(b.target).parents("#"+this.container_id).length?this.active_field=!0:this.close_field()},b.prototype.results_build=function(){var a,b,c,e,f;this.parsing=!0,this.results_data=d.SelectParser.select_to_array(this.form_field),this.is_multiple&&this.choices>0?(this.search_choices.find("li.search-choice").remove(),this.choices=0):this.is_multiple||(this.selected_item.find("span").text(this.default_text),this.form_field.options.length>this.disable_search_threshold?this.container.removeClass("chzn-container-single-nosearch"):this.container.addClass("chzn-container-single-nosearch")),a="",f=this.results_data;for(c=0,e=f.length;c<e;c++)b=f[c],b.group?a+=this.result_add_group(b):b.empty||(a+=this.result_add_option(b),b.selected&&this.is_multiple?this.choice_build(b):b.selected&&!this.is_multiple&&(this.selected_item.find("span").text(b.text),this.allow_single_deselect&&this.single_deselect_control_build()));return this.search_field_disabled(),this.show_search_field_default(),this.search_field_scale(),this.search_results.html(a),this.parsing=!1},b.prototype.result_add_group=function(b){return b.disabled?"":(b.dom_id=this.container_id+"_g_"+b.array_index,'<li id="'+b.dom_id+'" class="group-result">'+a("<div />").text(b.label).html()+"</li>")},b.prototype.result_do_highlight=function(a){var b,c,d,e,f;if(a.length){this.result_clear_highlight(),this.result_highlight=a,this.result_highlight.addClass("highlighted"),d=parseInt(this.search_results.css("maxHeight"),10),f=this.search_results.scrollTop(),e=d+f,c=this.result_highlight.position().top+this.search_results.scrollTop(),b=c+this.result_highlight.outerHeight();if(b>=e)return this.search_results.scrollTop(b-d>0?b-d:0);if(c<f)return this.search_results.scrollTop(c)}},b.prototype.result_clear_highlight=function(){return this.result_highlight&&this.result_highlight.removeClass("highlighted"),this.result_highlight=null},b.prototype.results_show=function(){var a;return this.is_multiple||(this.selected_item.addClass("chzn-single-with-drop"),this.result_single_selected&&this.result_do_highlight(this.result_single_selected)),a=this.is_multiple?this.container.height():this.container.height()-1,this.dropdown.css({top:a+"px",left:0}),this.results_showing=!0,this.search_field.focus(),this.search_field.val(this.search_field.val()),this.winnow_results()},b.prototype.results_hide=function(){return this.is_multiple||this.selected_item.removeClass("chzn-single-with-drop"),this.result_clear_highlight(),this.dropdown.css({left:"-9000px"}),this.results_showing=!1},b.prototype.set_tab_index=function(a){var b;if(this.form_field_jq.attr("tabindex"))return b=this.form_field_jq.attr("tabindex"),this.form_field_jq.attr("tabindex",-1),this.is_multiple?this.search_field.attr("tabindex",b):(this.selected_item.attr("tabindex",b),this.search_field.attr("tabindex",-1))},b.prototype.show_search_field_default=function(){return this.is_multiple&&this.choices<1&&!this.active_field?(this.search_field.val(this.default_text),this.search_field.addClass("default")):(this.search_field.val(""),this.search_field.removeClass("default"))},b.prototype.search_results_mouseup=function(b){var c;c=a(b.target).hasClass("active-result")?a(b.target):a(b.target).parents(".active-result").first();if(c.length)return this.result_highlight=c,this.result_select(b)},b.prototype.search_results_mouseover=function(b){var c;c=a(b.target).hasClass("active-result")?a(b.target):a(b.target).parents(".active-result").first();if(c)return this.result_do_highlight(c)},b.prototype.search_results_mouseout=function(b){if(a(b.target).hasClass("active-result"))return this.result_clear_highlight()},b.prototype.choices_click=function(b){b.preventDefault();if(this.active_field&&!a(b.target).hasClass("search-choice")&&!this.results_showing)return this.results_show()},b.prototype.choice_build=function(b){var c,d;return c=this.container_id+"_c_"+b.array_index,this.choices+=1,this.search_container.before('<li class="search-choice" id="'+c+'"><span>'+b.html+'</span><a href="javascript:void(0)" class="search-choice-close" rel="'+b.array_index+'"></a></li>'),d=a("#"+c).find("a").first(),d.click(g(function(a){return this.choice_destroy_link_click(a)},this))},b.prototype.choice_destroy_link_click=function(b){return b.preventDefault(),this.is_disabled?b.stopPropagation:(this.pending_destroy_click=!0,this.choice_destroy(a(b.target)))},b.prototype.choice_destroy=function(a){return this.choices-=1,this.show_search_field_default(),this.is_multiple&&this.choices>0&&this.search_field.val().length<1&&this.results_hide(),this.result_deselect(a.attr("rel")),a.parents("li").first().remove()},b.prototype.results_reset=function(b){this.form_field.options[0].selected=!0,this.selected_item.find("span").text(this.default_text),this.show_search_field_default(),a(b.target).remove(),this.form_field_jq.trigger("change");if(this.active_field)return this.results_hide()},b.prototype.result_select=function(a){var b,c,d,e;if(this.result_highlight)return b=this.result_highlight,c=b.attr("id"),this.result_clear_highlight(),this.is_multiple?this.result_deactivate(b):(this.search_results.find(".result-selected").removeClass("result-selected"),this.result_single_selected=b),b.addClass("result-selected"),e=c.substr(c.lastIndexOf("_")+1),d=this.results_data[e],d.selected=!0,this.form_field.options[d.options_index].selected=!0,this.is_multiple?this.choice_build(d):(this.selected_item.find("span").first().text(d.text),this.allow_single_deselect&&this.single_deselect_control_build()),(!a.metaKey||!this.is_multiple)&&this.results_hide(),this.search_field.val(""),this.form_field_jq.trigger("change"),this.search_field_scale()},b.prototype.result_activate=function(a){return a.addClass("active-result")},b.prototype.result_deactivate=function(a){return a.removeClass("active-result")},b.prototype.result_deselect=function(b){var c,d;return d=this.results_data[b],d.selected=!1,this.form_field.options[d.options_index].selected=!1,c=a("#"+this.container_id+"_o_"+b),c.removeClass("result-selected").addClass("active-result").show(),this.result_clear_highlight(),this.winnow_results(),this.form_field_jq.trigger("change"),this.search_field_scale()},b.prototype.single_deselect_control_build=function(){if(this.allow_single_deselect&&this.selected_item.find("abbr").length<1)return this.selected_item.find("span").first().after('<abbr class="search-choice-close"></abbr>')},b.prototype.winnow_results=function(){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r;this.no_results_clear(),i=0,j=this.search_field.val()===this.default_text?"":a("<div/>").text(a.trim(this.search_field.val())).html(),f=new RegExp("^"+j.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i"),m=new RegExp(j.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i"),r=this.results_data;for(n=0,p=r.length;n<p;n++){c=r[n];if(!c.disabled&&!c.empty)if(c.group)a("#"+c.dom_id).css("display","none");else if(!this.is_multiple||!c.selected){b=!1,h=c.dom_id,g=a("#"+h);if(f.test(c.html))b=!0,i+=1;else if(c.html.indexOf(" ")>=0||c.html.indexOf("[")===0){e=c.html.replace(/\[|\]/g,"").split(" ");if(e.length)for(o=0,q=e.length;o<q;o++)d=e[o],f.test(d)&&(b=!0,i+=1)}b?(j.length?(k=c.html.search(m),l=c.html.substr(0,k+j.length)+"</em>"+c.html.substr(k+j.length),l=l.substr(0,k)+"<em>"+l.substr(k)):l=c.html,g.html(l),this.result_activate(g),c.group_array_index!=null&&a("#"+this.results_data[c.group_array_index].dom_id).css("display","list-item")):(this.result_highlight&&h===this.result_highlight.attr("id")&&this.result_clear_highlight(),this.result_deactivate(g))}}return i<1&&j.length?this.no_results(j):this.winnow_results_set_highlight()},b.prototype.winnow_results_clear=function(){var b,c,d,e,f;this.search_field.val(""),c=this.search_results.find("li"),f=[];for(d=0,e=c.length;d<e;d++)b=c[d],b=a(b),f.push(b.hasClass("group-result")?b.css("display","auto"):!this.is_multiple||!b.hasClass("result-selected")?this.result_activate(b):void 0);return f},b.prototype.winnow_results_set_highlight=function(){var a,b;if(!this.result_highlight){b=this.is_multiple?[]:this.search_results.find(".result-selected.active-result"),a=b.length?b.first():this.search_results.find(".active-result").first();if(a!=null)return this.result_do_highlight(a)}},b.prototype.no_results=function(b){var c;return c=a('<li class="no-results">'+this.results_none_found+' "<span></span>"</li>'),c.find("span").first().html(b),this.search_results.append(c)},b.prototype.no_results_clear=function(){return this.search_results.find(".no-results").remove()},b.prototype.keydown_arrow=function(){var b,c;this.result_highlight?this.results_showing&&(c=this.result_highlight.nextAll("li.active-result").first(),c&&this.result_do_highlight(c)):(b=this.search_results.find("li.active-result").first(),b&&this.result_do_highlight(a(b)));if(!this.results_showing)return this.results_show()},b.prototype.keyup_arrow=function(){var a;if(!this.results_showing&&!this.is_multiple)return this.results_show();if(this.result_highlight)return a=this.result_highlight.prevAll("li.active-result"),a.length?this.result_do_highlight(a.first()):(this.choices>0&&this.results_hide(),this.result_clear_highlight())},b.prototype.keydown_backstroke=function(){return this.pending_backstroke?(this.choice_destroy(this.pending_backstroke.find("a").first()),this.clear_backstroke()):(this.pending_backstroke=this.search_container.siblings("li.search-choice").last(),this.pending_backstroke.addClass("search-choice-focus"))},b.prototype.clear_backstroke=function(){return this.pending_backstroke&&this.pending_backstroke.removeClass("search-choice-focus"),this.pending_backstroke=null},b.prototype.keydown_checker=function(a){var b,c;b=(c=a.which)!=null?c:a.keyCode,this.search_field_scale(),b!==8&&this.pending_backstroke&&this.clear_backstroke();switch(b){case 8:this.backstroke_length=this.search_field.val().length;break;case 9:this.results_showing&&!this.is_multiple&&this.result_select(a),this.mouse_on_container=!1;break;case 13:a.preventDefault();break;case 38:a.preventDefault(),this.keyup_arrow();break;case 40:this.keydown_arrow()}},b.prototype.search_field_scale=function(){var b,c,d,e,f,g,h,i,j;if(this.is_multiple){d=0,h=0,f="position:absolute; left: -1000px; top: -1000px; display:none;",g=["font-size","font-style","font-weight","font-family","line-height","text-transform","letter-spacing"];for(i=0,j=g.length;i<j;i++)e=g[i],f+=e+":"+this.search_field.css(e)+";";return c=a("<div />",{style:f}),c.text(this.search_field.val()),a("body").append(c),h=c.width()+25,c.remove(),h>this.f_width-10&&(h=this.f_width-10),this.search_field.css({width:h+"px"}),b=this.container.height(),this.dropdown.css({top:b+"px"})}},b.prototype.generate_random_id=function(){var b;b="sel"+this.generate_random_char()+this.generate_random_char()+this.generate_random_char();while(a("#"+b).length>0)b+=this.generate_random_char();return b},b}(),c=function(a){var b;return b=a.outerWidth()-a.width()},d.get_side_border_padding=c}.call(this) ((function(){var a;a=function(){function a(){this.options_index=0,this.parsed=[]}return a.prototype.add_node=function(a){return a.nodeName==="OPTGROUP"?this.add_group(a):this.add_option(a)},a.prototype.add_group=function(a){var b,c,d,e,f,g;b=this.parsed.length,this.parsed.push({array_index:b,group:!0,label:a.label,children:0,disabled:a.disabled}),f=a.childNodes,g=[];for(d=0,e=f.length;d<e;d++)c=f[d],g.push(this.add_option(c,b,a.disabled));return g},a.prototype.add_option=function(a,b,c){if(a.nodeName==="OPTION")return a.text!==""?(b!=null&&(this.parsed[b].children+=1),this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:a.value,text:a.text,html:a.innerHTML,selected:a.selected,disabled:c===!0?c:a.disabled,group_array_index:b,classes:a.className,style:a.style.cssText})):this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:!0}),this.options_index+=1},a}(),a.select_to_array=function(b){var c,d,e,f,g;d=new a,g=b.childNodes;for(e=0,f=g.length;e<f;e++)c=g[e],d.add_node(c);return d.parsed},this.SelectParser=a})).call(this),function(){var a,b;b=this,a=function(){function a(a,b){this.form_field=a,this.options=b!=null?b:{},this.set_default_values(),this.is_multiple=this.form_field.multiple,this.default_text_default=this.is_multiple?"Select Some Options":"Select an Option",this.setup(),this.set_up_html(),this.register_observers(),this.finish_setup()}return a.prototype.set_default_values=function(){var a=this;return this.click_test_action=function(b){return a.test_active_click(b)},this.activate_action=function(b){return a.activate_field(b)},this.active_field=!1,this.mouse_on_container=!1,this.results_showing=!1,this.result_highlighted=null,this.result_single_selected=null,this.allow_single_deselect=this.options.allow_single_deselect!=null&&this.form_field.options[0]!=null&&this.form_field.options[0].text===""?this.options.allow_single_deselect:!1,this.disable_search_threshold=this.options.disable_search_threshold||0,this.choices=0,this.results_none_found=this.options.no_results_text||"No results match"},a.prototype.mouse_enter=function(){return this.mouse_on_container=!0},a.prototype.mouse_leave=function(){return this.mouse_on_container=!1},a.prototype.input_focus=function(a){var b=this;if(!this.active_field)return setTimeout(function(){return b.container_mousedown()},50)},a.prototype.input_blur=function(a){var b=this;if(!this.mouse_on_container)return this.active_field=!1,setTimeout(function(){return b.blur_test()},100)},a.prototype.result_add_option=function(a){var b,c;return a.disabled?"":(a.dom_id=this.container_id+"_o_"+a.array_index,b=a.selected&&this.is_multiple?[]:["active-result"],a.selected&&b.push("result-selected"),a.group_array_index!=null&&b.push("group-option"),a.classes!==""&&b.push(a.classes),c=a.style.cssText!==""?' style="'+a.style+'"':"",'<li id="'+a.dom_id+'" class="'+b.join(" ")+'"'+c+">"+a.html+"</li>")},a.prototype.results_update_field=function(){return this.result_clear_highlight(),this.result_single_selected=null,this.results_build()},a.prototype.results_toggle=function(){return this.results_showing?this.results_hide():this.results_show()},a.prototype.results_search=function(a){return this.results_showing?this.winnow_results():this.results_show()},a.prototype.keyup_checker=function(a){var b,c;b=(c=a.which)!=null?c:a.keyCode,this.search_field_scale();switch(b){case 8:if(this.is_multiple&&this.backstroke_length<1&&this.choices>0)return this.keydown_backstroke();if(!this.pending_backstroke)return this.result_clear_highlight(),this.results_search();break;case 13:a.preventDefault();if(this.results_showing)return this.result_select(a);break;case 27:return this.results_showing&&this.results_hide(),!0;case 9:case 38:case 40:case 16:case 91:case 17:break;default:return this.results_search()}},a.prototype.generate_field_id=function(){var a;return a=this.generate_random_id(),this.form_field.id=a,a},a.prototype.generate_random_char=function(){var a,b,c;return a="0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZ",c=Math.floor(Math.random()*a.length),b=a.substring(c,c+1)},a}(),b.AbstractChosen=a}.call(this),function(){var a,b,c,d,e=Object.prototype.hasOwnProperty,f=function(a,b){function d(){this.constructor=a}for(var c in b)e.call(b,c)&&(a[c]=b[c]);return d.prototype=b.prototype,a.prototype=new d,a.__super__=b.prototype,a};d=this,a=jQuery,a.fn.extend({chosen:function(c){return!a.browser.msie||a.browser.version!=="6.0"&&a.browser.version!=="7.0"?a(this).each(function(d){if(!a(this).hasClass("chzn-done"))return new b(this,c)}):this}}),b=function(b){function e(){e.__super__.constructor.apply(this,arguments)}return f(e,b),e.prototype.setup=function(){return this.form_field_jq=a(this.form_field),this.is_rtl=this.form_field_jq.hasClass("chzn-rtl")},e.prototype.finish_setup=function(){return this.form_field_jq.addClass("chzn-done")},e.prototype.set_up_html=function(){var b,d,e,f;return this.container_id=this.form_field.id.length?this.form_field.id.replace(/(:|\.)/g,"_"):this.generate_field_id(),this.container_id+="_chzn",this.f_width=this.form_field_jq.outerWidth(),this.default_text=this.form_field_jq.data("placeholder")?this.form_field_jq.data("placeholder"):this.default_text_default,b=a("<div />",{id:this.container_id,"class":"chzn-container"+(this.is_rtl?" chzn-rtl":""),style:"width: "+this.f_width+"px;"}),this.is_multiple?b.html('<ul class="chzn-choices"><li class="search-field"><input type="text" value="'+this.default_text+'" class="default" autocomplete="off" style="width:25px;" /></li></ul><div class="chzn-drop" style="left:-9000px;"><ul class="chzn-results"></ul></div>'):b.html('<a href="javascript:void(0)" class="chzn-single"><span>'+this.default_text+'</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" autocomplete="off" /></div><ul class="chzn-results"></ul></div>'),this.form_field_jq.hide().after(b),this.container=a("#"+this.container_id),this.container.addClass("chzn-container-"+(this.is_multiple?"multi":"single")),this.dropdown=this.container.find("div.chzn-drop").first(),d=this.container.height(),e=this.f_width-c(this.dropdown),this.dropdown.css({width:e+"px",top:d+"px"}),this.search_field=this.container.find("input").first(),this.search_results=this.container.find("ul.chzn-results").first(),this.search_field_scale(),this.search_no_results=this.container.find("li.no-results").first(),this.is_multiple?(this.search_choices=this.container.find("ul.chzn-choices").first(),this.search_container=this.container.find("li.search-field").first()):(this.search_container=this.container.find("div.chzn-search").first(),this.selected_item=this.container.find(".chzn-single").first(),f=e-c(this.search_container)-c(this.search_field),this.search_field.css({width:f+"px"})),this.results_build(),this.set_tab_index(),this.form_field_jq.trigger("liszt:ready",{chosen:this})},e.prototype.register_observers=function(){var a=this;return this.container.mousedown(function(b){return a.container_mousedown(b)}),this.container.mouseup(function(b){return a.container_mouseup(b)}),this.container.mouseenter(function(b){return a.mouse_enter(b)}),this.container.mouseleave(function(b){return a.mouse_leave(b)}),this.search_results.mouseup(function(b){return a.search_results_mouseup(b)}),this.search_results.mouseover(function(b){return a.search_results_mouseover(b)}),this.search_results.mouseout(function(b){return a.search_results_mouseout(b)}),this.form_field_jq.bind("liszt:updated",function(b){return a.results_update_field(b)}),this.search_field.blur(function(b){return a.input_blur(b)}),this.search_field.keyup(function(b){return a.keyup_checker(b)}),this.search_field.keydown(function(b){return a.keydown_checker(b)}),this.is_multiple?(this.search_choices.click(function(b){return a.choices_click(b)}),this.search_field.focus(function(b){return a.input_focus(b)})):this.container.click(function(a){return a.preventDefault()})},e.prototype.search_field_disabled=function(){this.is_disabled=this.form_field_jq[0].disabled;if(this.is_disabled)return this.container.addClass("chzn-disabled"),this.search_field[0].disabled=!0,this.is_multiple||this.selected_item.unbind("focus",this.activate_action),this.close_field();this.container.removeClass("chzn-disabled"),this.search_field[0].disabled=!1;if(!this.is_multiple)return this.selected_item.bind("focus",this.activate_action)},e.prototype.container_mousedown=function(b){var c;if(!this.is_disabled)return c=b!=null?a(b.target).hasClass("search-choice-close"):!1,b&&b.type==="mousedown"&&b.stopPropagation(),!this.pending_destroy_click&&!c?(this.active_field?!this.is_multiple&&b&&(a(b.target)[0]===this.selected_item[0]||a(b.target).parents("a.chzn-single").length)&&(b.preventDefault(),this.results_toggle()):(this.is_multiple&&this.search_field.val(""),a(document).click(this.click_test_action),this.results_show()),this.activate_field()):this.pending_destroy_click=!1},e.prototype.container_mouseup=function(a){if(a.target.nodeName==="ABBR")return this.results_reset(a)},e.prototype.blur_test=function(a){if(!this.active_field&&this.container.hasClass("chzn-container-active"))return this.close_field()},e.prototype.close_field=function(){return a(document).unbind("click",this.click_test_action),this.is_multiple||(this.selected_item.attr("tabindex",this.search_field.attr("tabindex")),this.search_field.attr("tabindex",-1)),this.active_field=!1,this.results_hide(),this.container.removeClass("chzn-container-active"),this.winnow_results_clear(),this.clear_backstroke(),this.show_search_field_default(),this.search_field_scale()},e.prototype.activate_field=function(){return!this.is_multiple&&!this.active_field&&(this.search_field.attr("tabindex",this.selected_item.attr("tabindex")),this.selected_item.attr("tabindex",-1)),this.container.addClass("chzn-container-active"),this.active_field=!0,this.search_field.val(this.search_field.val()),this.search_field.focus()},e.prototype.test_active_click=function(b){return a(b.target).parents("#"+this.container_id).length?this.active_field=!0:this.close_field()},e.prototype.results_build=function(){var a,b,c,e,f;this.parsing=!0,this.results_data=d.SelectParser.select_to_array(this.form_field),this.is_multiple&&this.choices>0?(this.search_choices.find("li.search-choice").remove(),this.choices=0):this.is_multiple||(this.selected_item.find("span").text(this.default_text),this.form_field.options.length<=this.disable_search_threshold?this.container.addClass("chzn-container-single-nosearch"):this.container.removeClass("chzn-container-single-nosearch")),a="",f=this.results_data;for(c=0,e=f.length;c<e;c++)b=f[c],b.group?a+=this.result_add_group(b):b.empty||(a+=this.result_add_option(b),b.selected&&this.is_multiple?this.choice_build(b):b.selected&&!this.is_multiple&&(this.selected_item.find("span").text(b.text),this.allow_single_deselect&&this.single_deselect_control_build()));return this.search_field_disabled(),this.show_search_field_default(),this.search_field_scale(),this.search_results.html(a),this.parsing=!1},e.prototype.result_add_group=function(b){return b.disabled?"":(b.dom_id=this.container_id+"_g_"+b.array_index,'<li id="'+b.dom_id+'" class="group-result">'+a("<div />").text(b.label).html()+"</li>")},e.prototype.result_do_highlight=function(a){var b,c,d,e,f;if(a.length){this.result_clear_highlight(),this.result_highlight=a,this.result_highlight.addClass("highlighted"),d=parseInt(this.search_results.css("maxHeight"),10),f=this.search_results.scrollTop(),e=d+f,c=this.result_highlight.position().top+this.search_results.scrollTop(),b=c+this.result_highlight.outerHeight();if(b>=e)return this.search_results.scrollTop(b-d>0?b-d:0);if(c<f)return this.search_results.scrollTop(c)}},e.prototype.result_clear_highlight=function(){return this.result_highlight&&this.result_highlight.removeClass("highlighted"),this.result_highlight=null},e.prototype.results_show=function(){var a;return this.is_multiple||(this.selected_item.addClass("chzn-single-with-drop"),this.result_single_selected&&this.result_do_highlight(this.result_single_selected)),a=this.is_multiple?this.container.height():this.container.height()-1,this.dropdown.css({top:a+"px",left:0}),this.results_showing=!0,this.search_field.focus(),this.search_field.val(this.search_field.val()),this.winnow_results()},e.prototype.results_hide=function(){return this.is_multiple||this.selected_item.removeClass("chzn-single-with-drop"),this.result_clear_highlight(),this.dropdown.css({left:"-9000px"}),this.results_showing=!1},e.prototype.set_tab_index=function(a){var b;if(this.form_field_jq.attr("tabindex"))return b=this.form_field_jq.attr("tabindex"),this.form_field_jq.attr("tabindex",-1),this.is_multiple?this.search_field.attr("tabindex",b):(this.selected_item.attr("tabindex",b),this.search_field.attr("tabindex",-1))},e.prototype.show_search_field_default=function(){return this.is_multiple&&this.choices<1&&!this.active_field?(this.search_field.val(this.default_text),this.search_field.addClass("default")):(this.search_field.val(""),this.search_field.removeClass("default"))},e.prototype.search_results_mouseup=function(b){var c;c=a(b.target).hasClass("active-result")?a(b.target):a(b.target).parents(".active-result").first();if(c.length)return this.result_highlight=c,this.result_select(b)},e.prototype.search_results_mouseover=function(b){var c;c=a(b.target).hasClass("active-result")?a(b.target):a(b.target).parents(".active-result").first();if(c)return this.result_do_highlight(c)},e.prototype.search_results_mouseout=function(b){if(a(b.target).hasClass("active-result"))return this.result_clear_highlight()},e.prototype.choices_click=function(b){b.preventDefault();if(this.active_field&&!a(b.target).hasClass("search-choice")&&!this.results_showing)return this.results_show()},e.prototype.choice_build=function(b){var c,d,e=this;return c=this.container_id+"_c_"+b.array_index,this.choices+=1,this.search_container.before('<li class="search-choice" id="'+c+'"><span>'+b.html+'</span><a href="javascript:void(0)" class="search-choice-close" rel="'+b.array_index+'"></a></li>'),d=a("#"+c).find("a").first(),d.click(function(a){return e.choice_destroy_link_click(a)})},e.prototype.choice_destroy_link_click=function(b){return b.preventDefault(),this.is_disabled?b.stopPropagation:(this.pending_destroy_click=!0,this.choice_destroy(a(b.target)))},e.prototype.choice_destroy=function(a){return this.choices-=1,this.show_search_field_default(),this.is_multiple&&this.choices>0&&this.search_field.val().length<1&&this.results_hide(),this.result_deselect(a.attr("rel")),a.parents("li").first().remove()},e.prototype.results_reset=function(b){this.form_field.options[0].selected=!0,this.selected_item.find("span").text(this.default_text),this.show_search_field_default(),a(b.target).remove(),this.form_field_jq.trigger("change");if(this.active_field)return this.results_hide()},e.prototype.result_select=function(a){var b,c,d,e;if(this.result_highlight)return b=this.result_highlight,c=b.attr("id"),this.result_clear_highlight(),this.is_multiple?this.result_deactivate(b):(this.search_results.find(".result-selected").removeClass("result-selected"),this.result_single_selected=b),b.addClass("result-selected"),e=c.substr(c.lastIndexOf("_")+1),d=this.results_data[e],d.selected=!0,this.form_field.options[d.options_index].selected=!0,this.is_multiple?this.choice_build(d):(this.selected_item.find("span").first().text(d.text),this.allow_single_deselect&&this.single_deselect_control_build()),(!a.metaKey||!this.is_multiple)&&this.results_hide(),this.search_field.val(""),this.form_field_jq.trigger("change"),this.search_field_scale()},e.prototype.result_activate=function(a){return a.addClass("active-result")},e.prototype.result_deactivate=function(a){return a.removeClass("active-result")},e.prototype.result_deselect=function(b){var c,d;return d=this.results_data[b],d.selected=!1,this.form_field.options[d.options_index].selected=!1,c=a("#"+this.container_id+"_o_"+b),c.removeClass("result-selected").addClass("active-result").show(),this.result_clear_highlight(),this.winnow_results(),this.form_field_jq.trigger("change"),this.search_field_scale()},e.prototype.single_deselect_control_build=function(){if(this.allow_single_deselect&&this.selected_item.find("abbr").length<1)return this.selected_item.find("span").first().after('<abbr class="search-choice-close"></abbr>')},e.prototype.winnow_results=function(){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r;this.no_results_clear(),i=0,j=this.search_field.val()===this.default_text?"":a("<div/>").text(a.trim(this.search_field.val())).html(),f=new RegExp("^"+j.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i"),m=new RegExp(j.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i"),r=this.results_data;for(n=0,p=r.length;n<p;n++){c=r[n];if(!c.disabled&&!c.empty)if(c.group)a("#"+c.dom_id).css("display","none");else if(!this.is_multiple||!c.selected){b=!1,h=c.dom_id,g=a("#"+h);if(f.test(c.html))b=!0,i+=1;else if(c.html.indexOf(" ")>=0||c.html.indexOf("[")===0){e=c.html.replace(/\[|\]/g,"").split(" ");if(e.length)for(o=0,q=e.length;o<q;o++)d=e[o],f.test(d)&&(b=!0,i+=1)}b?(j.length?(k=c.html.search(m),l=c.html.substr(0,k+j.length)+"</em>"+c.html.substr(k+j.length),l=l.substr(0,k)+"<em>"+l.substr(k)):l=c.html,g.html(l),this.result_activate(g),c.group_array_index!=null&&a("#"+this.results_data[c.group_array_index].dom_id).css("display","list-item")):(this.result_highlight&&h===this.result_highlight.attr("id")&&this.result_clear_highlight(),this.result_deactivate(g))}}return i<1&&j.length?this.no_results(j):this.winnow_results_set_highlight()},e.prototype.winnow_results_clear=function(){var b,c,d,e,f;this.search_field.val(""),c=this.search_results.find("li"),f=[];for(d=0,e=c.length;d<e;d++)b=c[d],b=a(b),b.hasClass("group-result")?f.push(b.css("display","auto")):!this.is_multiple||!b.hasClass("result-selected")?f.push(this.result_activate(b)):f.push(void 0);return f},e.prototype.winnow_results_set_highlight=function(){var a,b;if(!this.result_highlight){b=this.is_multiple?[]:this.search_results.find(".result-selected.active-result"),a=b.length?b.first():this.search_results.find(".active-result").first();if(a!=null)return this.result_do_highlight(a)}},e.prototype.no_results=function(b){var c;return c=a('<li class="no-results">'+this.results_none_found+' "<span></span>"</li>'),c.find("span").first().html(b),this.search_results.append(c)},e.prototype.no_results_clear=function(){return this.search_results.find(".no-results").remove()},e.prototype.keydown_arrow=function(){var b,c;this.result_highlight?this.results_showing&&(c=this.result_highlight.nextAll("li.active-result").first(),c&&this.result_do_highlight(c)):(b=this.search_results.find("li.active-result").first(),b&&this.result_do_highlight(a(b)));if(!this.results_showing)return this.results_show()},e.prototype.keyup_arrow=function(){var a;if(!this.results_showing&&!this.is_multiple)return this.results_show();if(this.result_highlight)return a=this.result_highlight.prevAll("li.active-result"),a.length?this.result_do_highlight(a.first()):(this.choices>0&&this.results_hide(),this.result_clear_highlight())},e.prototype.keydown_backstroke=function(){return this.pending_backstroke?(this.choice_destroy(this.pending_backstroke.find("a").first()),this.clear_backstroke()):(this.pending_backstroke=this.search_container.siblings("li.search-choice").last(),this.pending_backstroke.addClass("search-choice-focus"))},e.prototype.clear_backstroke=function(){return this.pending_backstroke&&this.pending_backstroke.removeClass("search-choice-focus"),this.pending_backstroke=null},e.prototype.keydown_checker=function(a){var b,c;b=(c=a.which)!=null?c:a.keyCode,this.search_field_scale(),b!==8&&this.pending_backstroke&&this.clear_backstroke();switch(b){case 8:this.backstroke_length=this.search_field.val().length;break;case 9:this.results_showing&&!this.is_multiple&&this.result_select(a),this.mouse_on_container=!1;break;case 13:a.preventDefault();break;case 38:a.preventDefault(),this.keyup_arrow();break;case 40:this.keydown_arrow()}},e.prototype.search_field_scale=function(){var b,c,d,e,f,g,h,i,j;if(this.is_multiple){d=0,h=0,f="position:absolute; left: -1000px; top: -1000px; display:none;",g=["font-size","font-style","font-weight","font-family","line-height","text-transform","letter-spacing"];for(i=0,j=g.length;i<j;i++)e=g[i],f+=e+":"+this.search_field.css(e)+";";return c=a("<div />",{style:f}),c.text(this.search_field.val()),a("body").append(c),h=c.width()+25,c.remove(),h>this.f_width-10&&(h=this.f_width-10),this.search_field.css({width:h+"px"}),b=this.container.height(),this.dropdown.css({top:b+"px"})}},e.prototype.generate_random_id=function(){var b;b="sel"+this.generate_random_char()+this.generate_random_char()+this.generate_random_char();while(a("#"+b).length>0)b+=this.generate_random_char();return b},e}(AbstractChosen),c=function(a){var b;return b=a.outerWidth()-a.width()},d.get_side_border_padding=c}.call(this)

View File

@ -1,7 +1,7 @@
// Chosen, a Select Box Enhancer for jQuery and Protoype // Chosen, a Select Box Enhancer for jQuery and Protoype
// by Patrick Filler for Harvest, http://getharvest.com // by Patrick Filler for Harvest, http://getharvest.com
// //
// Version 0.9.5 // Version 0.9.7
// Full source at https://github.com/harvesthq/chosen // Full source at https://github.com/harvesthq/chosen
// Copyright (c) 2011 Harvest http://getharvest.com // Copyright (c) 2011 Harvest http://getharvest.com
@ -9,11 +9,14 @@
// This file is generated by `cake build`, do not edit it by hand. // This file is generated by `cake build`, do not edit it by hand.
(function() { (function() {
var SelectParser; var SelectParser;
SelectParser = (function() { SelectParser = (function() {
function SelectParser() { function SelectParser() {
this.options_index = 0; this.options_index = 0;
this.parsed = []; this.parsed = [];
} }
SelectParser.prototype.add_node = function(child) { SelectParser.prototype.add_node = function(child) {
if (child.nodeName === "OPTGROUP") { if (child.nodeName === "OPTGROUP") {
return this.add_group(child); return this.add_group(child);
@ -21,6 +24,7 @@
return this.add_option(child); return this.add_option(child);
} }
}; };
SelectParser.prototype.add_group = function(group) { SelectParser.prototype.add_group = function(group) {
var group_position, option, _i, _len, _ref, _results; var group_position, option, _i, _len, _ref, _results;
group_position = this.parsed.length; group_position = this.parsed.length;
@ -39,12 +43,11 @@
} }
return _results; return _results;
}; };
SelectParser.prototype.add_option = function(option, group_position, group_disabled) { SelectParser.prototype.add_option = function(option, group_position, group_disabled) {
if (option.nodeName === "OPTION") { if (option.nodeName === "OPTION") {
if (option.text !== "") { if (option.text !== "") {
if (group_position != null) { if (group_position != null) this.parsed[group_position].children += 1;
this.parsed[group_position].children += 1;
}
this.parsed.push({ this.parsed.push({
array_index: this.parsed.length, array_index: this.parsed.length,
options_index: this.options_index, options_index: this.options_index,
@ -67,8 +70,11 @@
return this.options_index += 1; return this.options_index += 1;
} }
}; };
return SelectParser; return SelectParser;
})(); })();
SelectParser.select_to_array = function(select) { SelectParser.select_to_array = function(select) {
var child, parser, _i, _len, _ref; var child, parser, _i, _len, _ref;
parser = new SelectParser(); parser = new SelectParser();
@ -79,17 +85,23 @@
} }
return parser.parsed; return parser.parsed;
}; };
this.SelectParser = SelectParser; this.SelectParser = SelectParser;
}).call(this); }).call(this);
/*
Chosen source: generate output using 'cake build'
Copyright (c) 2011 by Harvest
*/
(function() { (function() {
/*
Chosen source: generate output using 'cake build'
Copyright (c) 2011 by Harvest
*/
var AbstractChosen, root; var AbstractChosen, root;
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
root = this; root = this;
AbstractChosen = (function() { AbstractChosen = (function() {
function AbstractChosen(form_field, options) { function AbstractChosen(form_field, options) {
this.form_field = form_field; this.form_field = form_field;
this.options = options != null ? options : {}; this.options = options != null ? options : {};
@ -101,13 +113,15 @@
this.register_observers(); this.register_observers();
this.finish_setup(); this.finish_setup();
} }
AbstractChosen.prototype.set_default_values = function() { AbstractChosen.prototype.set_default_values = function() {
this.click_test_action = __bind(function(evt) { var _this = this;
return this.test_active_click(evt); this.click_test_action = function(evt) {
}, this); return _this.test_active_click(evt);
this.activate_action = __bind(function(evt) { };
return this.activate_field(evt); this.activate_action = function(evt) {
}, this); return _this.activate_field(evt);
};
this.active_field = false; this.active_field = false;
this.mouse_on_container = false; this.mouse_on_container = false;
this.results_showing = false; this.results_showing = false;
@ -118,52 +132,55 @@
this.choices = 0; this.choices = 0;
return this.results_none_found = this.options.no_results_text || "No results match"; return this.results_none_found = this.options.no_results_text || "No results match";
}; };
AbstractChosen.prototype.mouse_enter = function() { AbstractChosen.prototype.mouse_enter = function() {
return this.mouse_on_container = true; return this.mouse_on_container = true;
}; };
AbstractChosen.prototype.mouse_leave = function() { AbstractChosen.prototype.mouse_leave = function() {
return this.mouse_on_container = false; return this.mouse_on_container = false;
}; };
AbstractChosen.prototype.input_focus = function(evt) { AbstractChosen.prototype.input_focus = function(evt) {
var _this = this;
if (!this.active_field) { if (!this.active_field) {
return setTimeout((__bind(function() { return setTimeout((function() {
return this.container_mousedown(); return _this.container_mousedown();
}, this)), 50); }), 50);
} }
}; };
AbstractChosen.prototype.input_blur = function(evt) { AbstractChosen.prototype.input_blur = function(evt) {
var _this = this;
if (!this.mouse_on_container) { if (!this.mouse_on_container) {
this.active_field = false; this.active_field = false;
return setTimeout((__bind(function() { return setTimeout((function() {
return this.blur_test(); return _this.blur_test();
}, this)), 100); }), 100);
} }
}; };
AbstractChosen.prototype.result_add_option = function(option) { AbstractChosen.prototype.result_add_option = function(option) {
var classes, style; var classes, style;
if (!option.disabled) { if (!option.disabled) {
option.dom_id = this.container_id + "_o_" + option.array_index; option.dom_id = this.container_id + "_o_" + option.array_index;
classes = option.selected && this.is_multiple ? [] : ["active-result"]; classes = option.selected && this.is_multiple ? [] : ["active-result"];
if (option.selected) { if (option.selected) classes.push("result-selected");
classes.push("result-selected"); if (option.group_array_index != null) classes.push("group-option");
} if (option.classes !== "") classes.push(option.classes);
if (option.group_array_index != null) {
classes.push("group-option");
}
if (option.classes !== "") {
classes.push(option.classes);
}
style = option.style.cssText !== "" ? " style=\"" + option.style + "\"" : ""; style = option.style.cssText !== "" ? " style=\"" + option.style + "\"" : "";
return '<li id="' + option.dom_id + '" class="' + classes.join(' ') + '"' + style + '>' + option.html + '</li>'; return '<li id="' + option.dom_id + '" class="' + classes.join(' ') + '"' + style + '>' + option.html + '</li>';
} else { } else {
return ""; return "";
} }
}; };
AbstractChosen.prototype.results_update_field = function() { AbstractChosen.prototype.results_update_field = function() {
this.result_clear_highlight(); this.result_clear_highlight();
this.result_single_selected = null; this.result_single_selected = null;
return this.results_build(); return this.results_build();
}; };
AbstractChosen.prototype.results_toggle = function() { AbstractChosen.prototype.results_toggle = function() {
if (this.results_showing) { if (this.results_showing) {
return this.results_hide(); return this.results_hide();
@ -171,6 +188,7 @@
return this.results_show(); return this.results_show();
} }
}; };
AbstractChosen.prototype.results_search = function(evt) { AbstractChosen.prototype.results_search = function(evt) {
if (this.results_showing) { if (this.results_showing) {
return this.winnow_results(); return this.winnow_results();
@ -178,6 +196,7 @@
return this.results_show(); return this.results_show();
} }
}; };
AbstractChosen.prototype.keyup_checker = function(evt) { AbstractChosen.prototype.keyup_checker = function(evt) {
var stroke, _ref; var stroke, _ref;
stroke = (_ref = evt.which) != null ? _ref : evt.keyCode; stroke = (_ref = evt.which) != null ? _ref : evt.keyCode;
@ -193,15 +212,11 @@
break; break;
case 13: case 13:
evt.preventDefault(); evt.preventDefault();
if (this.results_showing) { if (this.results_showing) return this.result_select(evt);
return this.result_select(evt);
}
break; break;
case 27: case 27:
if (this.results_showing) { if (this.results_showing) this.results_hide();
return this.results_hide(); return true;
}
break;
case 9: case 9:
case 38: case 38:
case 40: case 40:
@ -213,48 +228,57 @@
return this.results_search(); return this.results_search();
} }
}; };
AbstractChosen.prototype.generate_field_id = function() { AbstractChosen.prototype.generate_field_id = function() {
var new_id; var new_id;
new_id = this.generate_random_id(); new_id = this.generate_random_id();
this.form_field.id = new_id; this.form_field.id = new_id;
return new_id; return new_id;
}; };
AbstractChosen.prototype.generate_random_char = function() { AbstractChosen.prototype.generate_random_char = function() {
var chars, newchar, rand; var chars, newchar, rand;
chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZ"; chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZ";
rand = Math.floor(Math.random() * chars.length); rand = Math.floor(Math.random() * chars.length);
return newchar = chars.substring(rand, rand + 1); return newchar = chars.substring(rand, rand + 1);
}; };
return AbstractChosen; return AbstractChosen;
})(); })();
root.AbstractChosen = AbstractChosen; root.AbstractChosen = AbstractChosen;
}).call(this); }).call(this);
/*
Chosen source: generate output using 'cake build'
Copyright (c) 2011 by Harvest
*/
(function() { (function() {
/* var Chosen, get_side_border_padding, root,
Chosen source: generate output using 'cake build' __hasProp = Object.prototype.hasOwnProperty,
Copyright (c) 2011 by Harvest __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
*/
var Chosen, get_side_border_padding, root;
var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
function ctor() { this.constructor = child; }
ctor.prototype = parent.prototype;
child.prototype = new ctor;
child.__super__ = parent.prototype;
return child;
}, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
root = this; root = this;
Chosen = (function() {
__extends(Chosen, AbstractChosen); Chosen = (function(_super) {
__extends(Chosen, _super);
function Chosen() { function Chosen() {
Chosen.__super__.constructor.apply(this, arguments); Chosen.__super__.constructor.apply(this, arguments);
} }
Chosen.prototype.setup = function() { Chosen.prototype.setup = function() {
return this.is_rtl = this.form_field.hasClassName("chzn-rtl"); return this.is_rtl = this.form_field.hasClassName("chzn-rtl");
}; };
Chosen.prototype.finish_setup = function() { Chosen.prototype.finish_setup = function() {
return this.form_field.addClassName("chzn-done"); return this.form_field.addClassName("chzn-done");
}; };
Chosen.prototype.set_default_values = function() { Chosen.prototype.set_default_values = function() {
Chosen.__super__.set_default_values.call(this); Chosen.__super__.set_default_values.call(this);
this.single_temp = new Template('<a href="javascript:void(0)" class="chzn-single"><span>#{default}</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" autocomplete="off" /></div><ul class="chzn-results"></ul></div>'); this.single_temp = new Template('<a href="javascript:void(0)" class="chzn-single"><span>#{default}</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" autocomplete="off" /></div><ul class="chzn-results"></ul></div>');
@ -262,6 +286,7 @@
this.choice_temp = new Template('<li class="search-choice" id="#{id}"><span>#{choice}</span><a href="javascript:void(0)" class="search-choice-close" rel="#{position}"></a></li>'); this.choice_temp = new Template('<li class="search-choice" id="#{id}"><span>#{choice}</span><a href="javascript:void(0)" class="search-choice-close" rel="#{position}"></a></li>');
return this.no_results_temp = new Template('<li class="no-results">' + this.results_none_found + ' "<span>#{terms}</span>"</li>'); return this.no_results_temp = new Template('<li class="no-results">' + this.results_none_found + ' "<span>#{terms}</span>"</li>');
}; };
Chosen.prototype.set_up_html = function() { Chosen.prototype.set_up_html = function() {
var base_template, container_props, dd_top, dd_width, sf_width; var base_template, container_props, dd_top, dd_width, sf_width;
this.container_id = this.form_field.identify().replace(/(:|\.)/g, '_') + "_chzn"; this.container_id = this.form_field.identify().replace(/(:|\.)/g, '_') + "_chzn";
@ -310,49 +335,56 @@
chosen: this chosen: this
}); });
}; };
Chosen.prototype.register_observers = function() { Chosen.prototype.register_observers = function() {
this.container.observe("mousedown", __bind(function(evt) { var _this = this;
return this.container_mousedown(evt); this.container.observe("mousedown", function(evt) {
}, this)); return _this.container_mousedown(evt);
this.container.observe("mouseup", __bind(function(evt) { });
return this.container_mouseup(evt); this.container.observe("mouseup", function(evt) {
}, this)); return _this.container_mouseup(evt);
this.container.observe("mouseenter", __bind(function(evt) { });
return this.mouse_enter(evt); this.container.observe("mouseenter", function(evt) {
}, this)); return _this.mouse_enter(evt);
this.container.observe("mouseleave", __bind(function(evt) { });
return this.mouse_leave(evt); this.container.observe("mouseleave", function(evt) {
}, this)); return _this.mouse_leave(evt);
this.search_results.observe("mouseup", __bind(function(evt) { });
return this.search_results_mouseup(evt); this.search_results.observe("mouseup", function(evt) {
}, this)); return _this.search_results_mouseup(evt);
this.search_results.observe("mouseover", __bind(function(evt) { });
return this.search_results_mouseover(evt); this.search_results.observe("mouseover", function(evt) {
}, this)); return _this.search_results_mouseover(evt);
this.search_results.observe("mouseout", __bind(function(evt) { });
return this.search_results_mouseout(evt); this.search_results.observe("mouseout", function(evt) {
}, this)); return _this.search_results_mouseout(evt);
this.form_field.observe("liszt:updated", __bind(function(evt) { });
return this.results_update_field(evt); this.form_field.observe("liszt:updated", function(evt) {
}, this)); return _this.results_update_field(evt);
this.search_field.observe("blur", __bind(function(evt) { });
return this.input_blur(evt); this.search_field.observe("blur", function(evt) {
}, this)); return _this.input_blur(evt);
this.search_field.observe("keyup", __bind(function(evt) { });
return this.keyup_checker(evt); this.search_field.observe("keyup", function(evt) {
}, this)); return _this.keyup_checker(evt);
this.search_field.observe("keydown", __bind(function(evt) { });
return this.keydown_checker(evt); this.search_field.observe("keydown", function(evt) {
}, this)); return _this.keydown_checker(evt);
});
if (this.is_multiple) { if (this.is_multiple) {
this.search_choices.observe("click", __bind(function(evt) { this.search_choices.observe("click", function(evt) {
return this.choices_click(evt); return _this.choices_click(evt);
}, this)); });
return this.search_field.observe("focus", __bind(function(evt) { return this.search_field.observe("focus", function(evt) {
return this.input_focus(evt); return _this.input_focus(evt);
}, this)); });
} else {
return this.container.observe("click", function(evt) {
return evt.preventDefault();
});
} }
}; };
Chosen.prototype.search_field_disabled = function() { Chosen.prototype.search_field_disabled = function() {
this.is_disabled = this.form_field.disabled; this.is_disabled = this.form_field.disabled;
if (this.is_disabled) { if (this.is_disabled) {
@ -370,18 +402,15 @@
} }
} }
}; };
Chosen.prototype.container_mousedown = function(evt) { Chosen.prototype.container_mousedown = function(evt) {
var target_closelink; var target_closelink;
if (!this.is_disabled) { if (!this.is_disabled) {
target_closelink = evt != null ? evt.target.hasClassName("search-choice-close") : false; target_closelink = evt != null ? evt.target.hasClassName("search-choice-close") : false;
if (evt && evt.type === "mousedown") { if (evt && evt.type === "mousedown") evt.stop();
evt.stop();
}
if (!this.pending_destroy_click && !target_closelink) { if (!this.pending_destroy_click && !target_closelink) {
if (!this.active_field) { if (!this.active_field) {
if (this.is_multiple) { if (this.is_multiple) this.search_field.clear();
this.search_field.clear();
}
document.observe("click", this.click_test_action); document.observe("click", this.click_test_action);
this.results_show(); this.results_show();
} else if (!this.is_multiple && evt && (evt.target === this.selected_item || evt.target.up("a.chzn-single"))) { } else if (!this.is_multiple && evt && (evt.target === this.selected_item || evt.target.up("a.chzn-single"))) {
@ -393,16 +422,17 @@
} }
} }
}; };
Chosen.prototype.container_mouseup = function(evt) { Chosen.prototype.container_mouseup = function(evt) {
if (evt.target.nodeName === "ABBR") { if (evt.target.nodeName === "ABBR") return this.results_reset(evt);
return this.results_reset(evt);
}
}; };
Chosen.prototype.blur_test = function(evt) { Chosen.prototype.blur_test = function(evt) {
if (!this.active_field && this.container.hasClassName("chzn-container-active")) { if (!this.active_field && this.container.hasClassName("chzn-container-active")) {
return this.close_field(); return this.close_field();
} }
}; };
Chosen.prototype.close_field = function() { Chosen.prototype.close_field = function() {
document.stopObserving("click", this.click_test_action); document.stopObserving("click", this.click_test_action);
if (!this.is_multiple) { if (!this.is_multiple) {
@ -417,6 +447,7 @@
this.show_search_field_default(); this.show_search_field_default();
return this.search_field_scale(); return this.search_field_scale();
}; };
Chosen.prototype.activate_field = function() { Chosen.prototype.activate_field = function() {
if (!this.is_multiple && !this.active_field) { if (!this.is_multiple && !this.active_field) {
this.search_field.tabIndex = this.selected_item.tabIndex; this.search_field.tabIndex = this.selected_item.tabIndex;
@ -427,6 +458,7 @@
this.search_field.value = this.search_field.value; this.search_field.value = this.search_field.value;
return this.search_field.focus(); return this.search_field.focus();
}; };
Chosen.prototype.test_active_click = function(evt) { Chosen.prototype.test_active_click = function(evt) {
if (evt.target.up('#' + this.container_id)) { if (evt.target.up('#' + this.container_id)) {
return this.active_field = true; return this.active_field = true;
@ -434,6 +466,7 @@
return this.close_field(); return this.close_field();
} }
}; };
Chosen.prototype.results_build = function() { Chosen.prototype.results_build = function() {
var content, data, _i, _len, _ref; var content, data, _i, _len, _ref;
this.parsing = true; this.parsing = true;
@ -461,9 +494,7 @@
this.choice_build(data); this.choice_build(data);
} else if (data.selected && !this.is_multiple) { } else if (data.selected && !this.is_multiple) {
this.selected_item.down("span").update(data.html); this.selected_item.down("span").update(data.html);
if (this.allow_single_deselect) { if (this.allow_single_deselect) this.single_deselect_control_build();
this.single_deselect_control_build();
}
} }
} }
} }
@ -473,6 +504,7 @@
this.search_results.update(content); this.search_results.update(content);
return this.parsing = false; return this.parsing = false;
}; };
Chosen.prototype.result_add_group = function(group) { Chosen.prototype.result_add_group = function(group) {
if (!group.disabled) { if (!group.disabled) {
group.dom_id = this.container_id + "_g_" + group.array_index; group.dom_id = this.container_id + "_g_" + group.array_index;
@ -481,6 +513,7 @@
return ""; return "";
} }
}; };
Chosen.prototype.result_do_highlight = function(el) { Chosen.prototype.result_do_highlight = function(el) {
var high_bottom, high_top, maxHeight, visible_bottom, visible_top; var high_bottom, high_top, maxHeight, visible_bottom, visible_top;
this.result_clear_highlight(); this.result_clear_highlight();
@ -497,12 +530,14 @@
return this.search_results.scrollTop = high_top; return this.search_results.scrollTop = high_top;
} }
}; };
Chosen.prototype.result_clear_highlight = function() { Chosen.prototype.result_clear_highlight = function() {
if (this.result_highlight) { if (this.result_highlight) {
this.result_highlight.removeClassName('highlighted'); this.result_highlight.removeClassName('highlighted');
} }
return this.result_highlight = null; return this.result_highlight = null;
}; };
Chosen.prototype.results_show = function() { Chosen.prototype.results_show = function() {
var dd_top; var dd_top;
if (!this.is_multiple) { if (!this.is_multiple) {
@ -521,6 +556,7 @@
this.search_field.value = this.search_field.value; this.search_field.value = this.search_field.value;
return this.winnow_results(); return this.winnow_results();
}; };
Chosen.prototype.results_hide = function() { Chosen.prototype.results_hide = function() {
if (!this.is_multiple) { if (!this.is_multiple) {
this.selected_item.removeClassName('chzn-single-with-drop'); this.selected_item.removeClassName('chzn-single-with-drop');
@ -531,6 +567,7 @@
}); });
return this.results_showing = false; return this.results_showing = false;
}; };
Chosen.prototype.set_tab_index = function(el) { Chosen.prototype.set_tab_index = function(el) {
var ti; var ti;
if (this.form_field.tabIndex) { if (this.form_field.tabIndex) {
@ -544,6 +581,7 @@
} }
} }
}; };
Chosen.prototype.show_search_field_default = function() { Chosen.prototype.show_search_field_default = function() {
if (this.is_multiple && this.choices < 1 && !this.active_field) { if (this.is_multiple && this.choices < 1 && !this.active_field) {
this.search_field.value = this.default_text; this.search_field.value = this.default_text;
@ -553,6 +591,7 @@
return this.search_field.removeClassName("default"); return this.search_field.removeClassName("default");
} }
}; };
Chosen.prototype.search_results_mouseup = function(evt) { Chosen.prototype.search_results_mouseup = function(evt) {
var target; var target;
target = evt.target.hasClassName("active-result") ? evt.target : evt.target.up(".active-result"); target = evt.target.hasClassName("active-result") ? evt.target : evt.target.up(".active-result");
@ -561,26 +600,29 @@
return this.result_select(evt); return this.result_select(evt);
} }
}; };
Chosen.prototype.search_results_mouseover = function(evt) { Chosen.prototype.search_results_mouseover = function(evt) {
var target; var target;
target = evt.target.hasClassName("active-result") ? evt.target : evt.target.up(".active-result"); target = evt.target.hasClassName("active-result") ? evt.target : evt.target.up(".active-result");
if (target) { if (target) return this.result_do_highlight(target);
return this.result_do_highlight(target);
}
}; };
Chosen.prototype.search_results_mouseout = function(evt) { Chosen.prototype.search_results_mouseout = function(evt) {
if (evt.target.hasClassName('active-result') || evt.target.up('.active-result')) { if (evt.target.hasClassName('active-result') || evt.target.up('.active-result')) {
return this.result_clear_highlight(); return this.result_clear_highlight();
} }
}; };
Chosen.prototype.choices_click = function(evt) { Chosen.prototype.choices_click = function(evt) {
evt.preventDefault(); evt.preventDefault();
if (this.active_field && !(evt.target.hasClassName('search-choice') || evt.target.up('.search-choice')) && !this.results_showing) { if (this.active_field && !(evt.target.hasClassName('search-choice') || evt.target.up('.search-choice')) && !this.results_showing) {
return this.results_show(); return this.results_show();
} }
}; };
Chosen.prototype.choice_build = function(item) { Chosen.prototype.choice_build = function(item) {
var choice_id, link; var choice_id, link,
_this = this;
choice_id = this.container_id + "_c_" + item.array_index; choice_id = this.container_id + "_c_" + item.array_index;
this.choices += 1; this.choices += 1;
this.search_container.insert({ this.search_container.insert({
@ -591,10 +633,11 @@
}) })
}); });
link = $(choice_id).down('a'); link = $(choice_id).down('a');
return link.observe("click", __bind(function(evt) { return link.observe("click", function(evt) {
return this.choice_destroy_link_click(evt); return _this.choice_destroy_link_click(evt);
}, this)); });
}; };
Chosen.prototype.choice_destroy_link_click = function(evt) { Chosen.prototype.choice_destroy_link_click = function(evt) {
evt.preventDefault(); evt.preventDefault();
if (!this.is_disabled) { if (!this.is_disabled) {
@ -602,6 +645,7 @@
return this.choice_destroy(evt.target); return this.choice_destroy(evt.target);
} }
}; };
Chosen.prototype.choice_destroy = function(link) { Chosen.prototype.choice_destroy = function(link) {
this.choices -= 1; this.choices -= 1;
this.show_search_field_default(); this.show_search_field_default();
@ -611,18 +655,16 @@
this.result_deselect(link.readAttribute("rel")); this.result_deselect(link.readAttribute("rel"));
return link.up('li').remove(); return link.up('li').remove();
}; };
Chosen.prototype.results_reset = function(evt) { Chosen.prototype.results_reset = function(evt) {
this.form_field.options[0].selected = true; this.form_field.options[0].selected = true;
this.selected_item.down("span").update(this.default_text); this.selected_item.down("span").update(this.default_text);
this.show_search_field_default(); this.show_search_field_default();
evt.target.remove(); evt.target.remove();
if (typeof Event.simulate === 'function') { if (typeof Event.simulate === 'function') this.form_field.simulate("change");
this.form_field.simulate("change"); if (this.active_field) return this.results_hide();
}
if (this.active_field) {
return this.results_hide();
}
}; };
Chosen.prototype.result_select = function(evt) { Chosen.prototype.result_select = function(evt) {
var high, item, position; var high, item, position;
if (this.result_highlight) { if (this.result_highlight) {
@ -643,13 +685,9 @@
this.choice_build(item); this.choice_build(item);
} else { } else {
this.selected_item.down("span").update(item.html); this.selected_item.down("span").update(item.html);
if (this.allow_single_deselect) { if (this.allow_single_deselect) this.single_deselect_control_build();
this.single_deselect_control_build();
}
}
if (!(evt.metaKey && this.is_multiple)) {
this.results_hide();
} }
if (!(evt.metaKey && this.is_multiple)) this.results_hide();
this.search_field.value = ""; this.search_field.value = "";
if (typeof Event.simulate === 'function') { if (typeof Event.simulate === 'function') {
this.form_field.simulate("change"); this.form_field.simulate("change");
@ -657,12 +695,15 @@
return this.search_field_scale(); return this.search_field_scale();
} }
}; };
Chosen.prototype.result_activate = function(el) { Chosen.prototype.result_activate = function(el) {
return el.addClassName("active-result"); return el.addClassName("active-result");
}; };
Chosen.prototype.result_deactivate = function(el) { Chosen.prototype.result_deactivate = function(el) {
return el.removeClassName("active-result"); return el.removeClassName("active-result");
}; };
Chosen.prototype.result_deselect = function(pos) { Chosen.prototype.result_deselect = function(pos) {
var result, result_data; var result, result_data;
result_data = this.results_data[pos]; result_data = this.results_data[pos];
@ -672,11 +713,10 @@
result.removeClassName("result-selected").addClassName("active-result").show(); result.removeClassName("result-selected").addClassName("active-result").show();
this.result_clear_highlight(); this.result_clear_highlight();
this.winnow_results(); this.winnow_results();
if (typeof Event.simulate === 'function') { if (typeof Event.simulate === 'function') this.form_field.simulate("change");
this.form_field.simulate("change");
}
return this.search_field_scale(); return this.search_field_scale();
}; };
Chosen.prototype.single_deselect_control_build = function() { Chosen.prototype.single_deselect_control_build = function() {
if (this.allow_single_deselect && !this.selected_item.down("abbr")) { if (this.allow_single_deselect && !this.selected_item.down("abbr")) {
return this.selected_item.down("span").insert({ return this.selected_item.down("span").insert({
@ -684,6 +724,7 @@
}); });
} }
}; };
Chosen.prototype.winnow_results = function() { Chosen.prototype.winnow_results = function() {
var found, option, part, parts, regex, result_id, results, searchText, startpos, text, zregex, _i, _j, _len, _len2, _ref; var found, option, part, parts, regex, result_id, results, searchText, startpos, text, zregex, _i, _j, _len, _len2, _ref;
this.no_results_clear(); this.no_results_clear();
@ -723,9 +764,7 @@
} else { } else {
text = option.html; text = option.html;
} }
if ($(result_id).innerHTML !== text) { if ($(result_id).innerHTML !== text) $(result_id).update(text);
$(result_id).update(text);
}
this.result_activate($(result_id)); this.result_activate($(result_id));
if (option.group_array_index != null) { if (option.group_array_index != null) {
$(this.results_data[option.group_array_index].dom_id).setStyle({ $(this.results_data[option.group_array_index].dom_id).setStyle({
@ -747,6 +786,7 @@
return this.winnow_results_set_highlight(); return this.winnow_results_set_highlight();
} }
}; };
Chosen.prototype.winnow_results_clear = function() { Chosen.prototype.winnow_results_clear = function() {
var li, lis, _i, _len, _results; var li, lis, _i, _len, _results;
this.search_field.clear(); this.search_field.clear();
@ -754,10 +794,17 @@
_results = []; _results = [];
for (_i = 0, _len = lis.length; _i < _len; _i++) { for (_i = 0, _len = lis.length; _i < _len; _i++) {
li = lis[_i]; li = lis[_i];
_results.push(li.hasClassName("group-result") ? li.show() : !this.is_multiple || !li.hasClassName("result-selected") ? this.result_activate(li) : void 0); if (li.hasClassName("group-result")) {
_results.push(li.show());
} else if (!this.is_multiple || !li.hasClassName("result-selected")) {
_results.push(this.result_activate(li));
} else {
_results.push(void 0);
}
} }
return _results; return _results;
}; };
Chosen.prototype.winnow_results_set_highlight = function() { Chosen.prototype.winnow_results_set_highlight = function() {
var do_high; var do_high;
if (!this.result_highlight) { if (!this.result_highlight) {
@ -767,16 +814,16 @@
if (!(do_high != null)) { if (!(do_high != null)) {
do_high = this.search_results.down(".active-result"); do_high = this.search_results.down(".active-result");
} }
if (do_high != null) { if (do_high != null) return this.result_do_highlight(do_high);
return this.result_do_highlight(do_high);
}
} }
}; };
Chosen.prototype.no_results = function(terms) { Chosen.prototype.no_results = function(terms) {
return this.search_results.insert(this.no_results_temp.evaluate({ return this.search_results.insert(this.no_results_temp.evaluate({
terms: terms terms: terms
})); }));
}; };
Chosen.prototype.no_results_clear = function() { Chosen.prototype.no_results_clear = function() {
var nr, _results; var nr, _results;
nr = null; nr = null;
@ -786,6 +833,7 @@
} }
return _results; return _results;
}; };
Chosen.prototype.keydown_arrow = function() { Chosen.prototype.keydown_arrow = function() {
var actives, nexts, sibs; var actives, nexts, sibs;
actives = this.search_results.select("li.active-result"); actives = this.search_results.select("li.active-result");
@ -795,15 +843,12 @@
} else if (this.results_showing) { } else if (this.results_showing) {
sibs = this.result_highlight.nextSiblings(); sibs = this.result_highlight.nextSiblings();
nexts = sibs.intersect(actives); nexts = sibs.intersect(actives);
if (nexts.length) { if (nexts.length) this.result_do_highlight(nexts.first());
this.result_do_highlight(nexts.first());
}
}
if (!this.results_showing) {
return this.results_show();
} }
if (!this.results_showing) return this.results_show();
} }
}; };
Chosen.prototype.keyup_arrow = function() { Chosen.prototype.keyup_arrow = function() {
var actives, prevs, sibs; var actives, prevs, sibs;
if (!this.results_showing && !this.is_multiple) { if (!this.results_showing && !this.is_multiple) {
@ -815,13 +860,12 @@
if (prevs.length) { if (prevs.length) {
return this.result_do_highlight(prevs.first()); return this.result_do_highlight(prevs.first());
} else { } else {
if (this.choices > 0) { if (this.choices > 0) this.results_hide();
this.results_hide();
}
return this.result_clear_highlight(); return this.result_clear_highlight();
} }
} }
}; };
Chosen.prototype.keydown_backstroke = function() { Chosen.prototype.keydown_backstroke = function() {
if (this.pending_backstroke) { if (this.pending_backstroke) {
this.choice_destroy(this.pending_backstroke.down("a")); this.choice_destroy(this.pending_backstroke.down("a"));
@ -831,27 +875,25 @@
return this.pending_backstroke.addClassName("search-choice-focus"); return this.pending_backstroke.addClassName("search-choice-focus");
} }
}; };
Chosen.prototype.clear_backstroke = function() { Chosen.prototype.clear_backstroke = function() {
if (this.pending_backstroke) { if (this.pending_backstroke) {
this.pending_backstroke.removeClassName("search-choice-focus"); this.pending_backstroke.removeClassName("search-choice-focus");
} }
return this.pending_backstroke = null; return this.pending_backstroke = null;
}; };
Chosen.prototype.keydown_checker = function(evt) { Chosen.prototype.keydown_checker = function(evt) {
var stroke, _ref; var stroke, _ref;
stroke = (_ref = evt.which) != null ? _ref : evt.keyCode; stroke = (_ref = evt.which) != null ? _ref : evt.keyCode;
this.search_field_scale(); this.search_field_scale();
if (stroke !== 8 && this.pending_backstroke) { if (stroke !== 8 && this.pending_backstroke) this.clear_backstroke();
this.clear_backstroke();
}
switch (stroke) { switch (stroke) {
case 8: case 8:
this.backstroke_length = this.search_field.value.length; this.backstroke_length = this.search_field.value.length;
break; break;
case 9: case 9:
if (this.results_showing && !this.is_multiple) { if (this.results_showing && !this.is_multiple) this.result_select(evt);
this.result_select(evt);
}
this.mouse_on_container = false; this.mouse_on_container = false;
break; break;
case 13: case 13:
@ -866,6 +908,7 @@
break; break;
} }
}; };
Chosen.prototype.search_field_scale = function() { Chosen.prototype.search_field_scale = function() {
var dd_top, div, h, style, style_block, styles, w, _i, _len; var dd_top, div, h, style, style_block, styles, w, _i, _len;
if (this.is_multiple) { if (this.is_multiple) {
@ -883,9 +926,7 @@
document.body.appendChild(div); document.body.appendChild(div);
w = Element.measure(div, 'width') + 25; w = Element.measure(div, 'width') + 25;
div.remove(); div.remove();
if (w > this.f_width - 10) { if (w > this.f_width - 10) w = this.f_width - 10;
w = this.f_width - 10;
}
this.search_field.setStyle({ this.search_field.setStyle({
'width': w + 'px' 'width': w + 'px'
}); });
@ -895,18 +936,25 @@
}); });
} }
}; };
return Chosen; return Chosen;
})();
})(AbstractChosen);
root.Chosen = Chosen; root.Chosen = Chosen;
if (Prototype.Browser.IE) { if (Prototype.Browser.IE) {
if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) { if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) {
Prototype.BrowserFeatures['Version'] = new Number(RegExp.$1); Prototype.BrowserFeatures['Version'] = new Number(RegExp.$1);
} }
} }
get_side_border_padding = function(elmt) { get_side_border_padding = function(elmt) {
var layout, side_border_padding; var layout, side_border_padding;
layout = new Element.Layout(elmt); layout = new Element.Layout(elmt);
return side_border_padding = layout.get("border-left") + layout.get("border-right") + layout.get("padding-left") + layout.get("padding-right"); return side_border_padding = layout.get("border-left") + layout.get("border-right") + layout.get("padding-left") + layout.get("padding-right");
}; };
root.get_side_border_padding = get_side_border_padding; root.get_side_border_padding = get_side_border_padding;
}).call(this); }).call(this);

View File

@ -1,10 +1,10 @@
// Chosen, a Select Box Enhancer for jQuery and Protoype // Chosen, a Select Box Enhancer for jQuery and Protoype
// by Patrick Filler for Harvest, http://getharvest.com // by Patrick Filler for Harvest, http://getharvest.com
// //
// Version 0.9.5 // Version 0.9.7
// Full source at https://github.com/harvesthq/chosen // Full source at https://github.com/harvesthq/chosen
// Copyright (c) 2011 Harvest http://getharvest.com // Copyright (c) 2011 Harvest http://getharvest.com
// MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md // MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
// This file is generated by `cake build`, do not edit it by hand. // This file is generated by `cake build`, do not edit it by hand.
(function(){var a;a=function(){function a(){this.options_index=0,this.parsed=[]}return a.prototype.add_node=function(a){return a.nodeName==="OPTGROUP"?this.add_group(a):this.add_option(a)},a.prototype.add_group=function(a){var b,c,d,e,f,g;b=this.parsed.length,this.parsed.push({array_index:b,group:!0,label:a.label,children:0,disabled:a.disabled}),f=a.childNodes,g=[];for(d=0,e=f.length;d<e;d++)c=f[d],g.push(this.add_option(c,b,a.disabled));return g},a.prototype.add_option=function(a,b,c){if(a.nodeName==="OPTION")return a.text!==""?(b!=null&&(this.parsed[b].children+=1),this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:a.value,text:a.text,html:a.innerHTML,selected:a.selected,disabled:c===!0?c:a.disabled,group_array_index:b,classes:a.className,style:a.style.cssText})):this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:!0}),this.options_index+=1},a}(),a.select_to_array=function(b){var c,d,e,f,g;d=new a,g=b.childNodes;for(e=0,f=g.length;e<f;e++)c=g[e],d.add_node(c);return d.parsed},this.SelectParser=a}).call(this),function(){var a,b,c=function(a,b){return function(){return a.apply(b,arguments)}};b=this,a=function(){function a(a,b){this.form_field=a,this.options=b!=null?b:{},this.set_default_values(),this.is_multiple=this.form_field.multiple,this.default_text_default=this.is_multiple?"Select Some Options":"Select an Option",this.setup(),this.set_up_html(),this.register_observers(),this.finish_setup()}return a.prototype.set_default_values=function(){return this.click_test_action=c(function(a){return this.test_active_click(a)},this),this.activate_action=c(function(a){return this.activate_field(a)},this),this.active_field=!1,this.mouse_on_container=!1,this.results_showing=!1,this.result_highlighted=null,this.result_single_selected=null,this.allow_single_deselect=this.options.allow_single_deselect!=null&&this.form_field.options[0]!=null&&this.form_field.options[0].text===""?this.options.allow_single_deselect:!1,this.disable_search_threshold=this.options.disable_search_threshold||0,this.choices=0,this.results_none_found=this.options.no_results_text||"No results match"},a.prototype.mouse_enter=function(){return this.mouse_on_container=!0},a.prototype.mouse_leave=function(){return this.mouse_on_container=!1},a.prototype.input_focus=function(a){if(!this.active_field)return setTimeout(c(function(){return this.container_mousedown()},this),50)},a.prototype.input_blur=function(a){if(!this.mouse_on_container)return this.active_field=!1,setTimeout(c(function(){return this.blur_test()},this),100)},a.prototype.result_add_option=function(a){var b,c;return a.disabled?"":(a.dom_id=this.container_id+"_o_"+a.array_index,b=a.selected&&this.is_multiple?[]:["active-result"],a.selected&&b.push("result-selected"),a.group_array_index!=null&&b.push("group-option"),a.classes!==""&&b.push(a.classes),c=a.style.cssText!==""?' style="'+a.style+'"':"",'<li id="'+a.dom_id+'" class="'+b.join(" ")+'"'+c+">"+a.html+"</li>")},a.prototype.results_update_field=function(){return this.result_clear_highlight(),this.result_single_selected=null,this.results_build()},a.prototype.results_toggle=function(){return this.results_showing?this.results_hide():this.results_show()},a.prototype.results_search=function(a){return this.results_showing?this.winnow_results():this.results_show()},a.prototype.keyup_checker=function(a){var b,c;b=(c=a.which)!=null?c:a.keyCode,this.search_field_scale();switch(b){case 8:if(this.is_multiple&&this.backstroke_length<1&&this.choices>0)return this.keydown_backstroke();if(!this.pending_backstroke)return this.result_clear_highlight(),this.results_search();break;case 13:a.preventDefault();if(this.results_showing)return this.result_select(a);break;case 27:if(this.results_showing)return this.results_hide();break;case 9:case 38:case 40:case 16:case 91:case 17:break;default:return this.results_search()}},a.prototype.generate_field_id=function(){var a;return a=this.generate_random_id(),this.form_field.id=a,a},a.prototype.generate_random_char=function(){var a,b,c;return a="0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZ",c=Math.floor(Math.random()*a.length),b=a.substring(c,c+1)},a}(),b.AbstractChosen=a}.call(this),function(){var a,b,c,d=Object.prototype.hasOwnProperty,e=function(a,b){function e(){this.constructor=a}for(var c in b)d.call(b,c)&&(a[c]=b[c]);return e.prototype=b.prototype,a.prototype=new e,a.__super__=b.prototype,a},f=function(a,b){return function(){return a.apply(b,arguments)}};c=this,a=function(){function a(){a.__super__.constructor.apply(this,arguments)}return e(a,AbstractChosen),a.prototype.setup=function(){return this.is_rtl=this.form_field.hasClassName("chzn-rtl")},a.prototype.finish_setup=function(){return this.form_field.addClassName("chzn-done")},a.prototype.set_default_values=function(){return a.__super__.set_default_values.call(this),this.single_temp=new Template('<a href="javascript:void(0)" class="chzn-single"><span>#{default}</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" autocomplete="off" /></div><ul class="chzn-results"></ul></div>'),this.multi_temp=new Template('<ul class="chzn-choices"><li class="search-field"><input type="text" value="#{default}" class="default" autocomplete="off" style="width:25px;" /></li></ul><div class="chzn-drop" style="left:-9000px;"><ul class="chzn-results"></ul></div>'),this.choice_temp=new Template('<li class="search-choice" id="#{id}"><span>#{choice}</span><a href="javascript:void(0)" class="search-choice-close" rel="#{position}"></a></li>'),this.no_results_temp=new Template('<li class="no-results">'+this.results_none_found+' "<span>#{terms}</span>"</li>')},a.prototype.set_up_html=function(){var a,c,d,e,f;return this.container_id=this.form_field.identify().replace(/(:|\.)/g,"_")+"_chzn",this.f_width=this.form_field.getStyle("width")?parseInt(this.form_field.getStyle("width"),10):this.form_field.getWidth(),c={id:this.container_id,"class":"chzn-container"+(this.is_rtl?" chzn-rtl":""),style:"width: "+this.f_width+"px"},this.default_text=this.form_field.readAttribute("data-placeholder")?this.form_field.readAttribute("data-placeholder"):this.default_text_default,a=this.is_multiple?(new Element("div",c)).update(this.multi_temp.evaluate({"default":this.default_text})):(new Element("div",c)).update(this.single_temp.evaluate({"default":this.default_text})),this.form_field.hide().insert({after:a}),this.container=$(this.container_id),this.container.addClassName("chzn-container-"+(this.is_multiple?"multi":"single")),this.dropdown=this.container.down("div.chzn-drop"),d=this.container.getHeight(),e=this.f_width-b(this.dropdown),this.dropdown.setStyle({width:e+"px",top:d+"px"}),this.search_field=this.container.down("input"),this.search_results=this.container.down("ul.chzn-results"),this.search_field_scale(),this.search_no_results=this.container.down("li.no-results"),this.is_multiple?(this.search_choices=this.container.down("ul.chzn-choices"),this.search_container=this.container.down("li.search-field")):(this.search_container=this.container.down("div.chzn-search"),this.selected_item=this.container.down(".chzn-single"),f=e-b(this.search_container)-b(this.search_field),this.search_field.setStyle({width:f+"px"})),this.results_build(),this.set_tab_index(),this.form_field.fire("liszt:ready",{chosen:this})},a.prototype.register_observers=function(){this.container.observe("mousedown",f(function(a){return this.container_mousedown(a)},this)),this.container.observe("mouseup",f(function(a){return this.container_mouseup(a)},this)),this.container.observe("mouseenter",f(function(a){return this.mouse_enter(a)},this)),this.container.observe("mouseleave",f(function(a){return this.mouse_leave(a)},this)),this.search_results.observe("mouseup",f(function(a){return this.search_results_mouseup(a)},this)),this.search_results.observe("mouseover",f(function(a){return this.search_results_mouseover(a)},this)),this.search_results.observe("mouseout",f(function(a){return this.search_results_mouseout(a)},this)),this.form_field.observe("liszt:updated",f(function(a){return this.results_update_field(a)},this)),this.search_field.observe("blur",f(function(a){return this.input_blur(a)},this)),this.search_field.observe("keyup",f(function(a){return this.keyup_checker(a)},this)),this.search_field.observe("keydown",f(function(a){return this.keydown_checker(a)},this));if(this.is_multiple)return this.search_choices.observe("click",f(function(a){return this.choices_click(a)},this)),this.search_field.observe("focus",f(function(a){return this.input_focus(a)},this))},a.prototype.search_field_disabled=function(){this.is_disabled=this.form_field.disabled;if(this.is_disabled)return this.container.addClassName("chzn-disabled"),this.search_field.disabled=!0,this.is_multiple||this.selected_item.stopObserving("focus",this.activate_action),this.close_field();this.container.removeClassName("chzn-disabled"),this.search_field.disabled=!1;if(!this.is_multiple)return this.selected_item.observe("focus",this.activate_action)},a.prototype.container_mousedown=function(a){var b;if(!this.is_disabled)return b=a!=null?a.target.hasClassName("search-choice-close"):!1,a&&a.type==="mousedown"&&a.stop(),!this.pending_destroy_click&&!b?(this.active_field?!this.is_multiple&&a&&(a.target===this.selected_item||a.target.up("a.chzn-single"))&&this.results_toggle():(this.is_multiple&&this.search_field.clear(),document.observe("click",this.click_test_action),this.results_show()),this.activate_field()):this.pending_destroy_click=!1},a.prototype.container_mouseup=function(a){if(a.target.nodeName==="ABBR")return this.results_reset(a)},a.prototype.blur_test=function(a){if(!this.active_field&&this.container.hasClassName("chzn-container-active"))return this.close_field()},a.prototype.close_field=function(){return document.stopObserving("click",this.click_test_action),this.is_multiple||(this.selected_item.tabIndex=this.search_field.tabIndex,this.search_field.tabIndex=-1),this.active_field=!1,this.results_hide(),this.container.removeClassName("chzn-container-active"),this.winnow_results_clear(),this.clear_backstroke(),this.show_search_field_default(),this.search_field_scale()},a.prototype.activate_field=function(){return!this.is_multiple&&!this.active_field&&(this.search_field.tabIndex=this.selected_item.tabIndex,this.selected_item.tabIndex=-1),this.container.addClassName("chzn-container-active"),this.active_field=!0,this.search_field.value=this.search_field.value,this.search_field.focus()},a.prototype.test_active_click=function(a){return a.target.up("#"+this.container_id)?this.active_field=!0:this.close_field()},a.prototype.results_build=function(){var a,b,d,e,f;this.parsing=!0,this.results_data=c.SelectParser.select_to_array(this.form_field),this.is_multiple&&this.choices>0?(this.search_choices.select("li.search-choice").invoke("remove"),this.choices=0):this.is_multiple||(this.selected_item.down("span").update(this.default_text),this.form_field.options.length>this.disable_search_threshold?this.container.removeClassName("chzn-container-single-nosearch"):this.container.addClassName("chzn-container-single-nosearch")),a="",f=this.results_data;for(d=0,e=f.length;d<e;d++)b=f[d],b.group?a+=this.result_add_group(b):b.empty||(a+=this.result_add_option(b),b.selected&&this.is_multiple?this.choice_build(b):b.selected&&!this.is_multiple&&(this.selected_item.down("span").update(b.html),this.allow_single_deselect&&this.single_deselect_control_build()));return this.search_field_disabled(),this.show_search_field_default(),this.search_field_scale(),this.search_results.update(a),this.parsing=!1},a.prototype.result_add_group=function(a){return a.disabled?"":(a.dom_id=this.container_id+"_g_"+a.array_index,'<li id="'+a.dom_id+'" class="group-result">'+a.label.escapeHTML()+"</li>")},a.prototype.result_do_highlight=function(a){var b,c,d,e,f;this.result_clear_highlight(),this.result_highlight=a,this.result_highlight.addClassName("highlighted"),d=parseInt(this.search_results.getStyle("maxHeight"),10),f=this.search_results.scrollTop,e=d+f,c=this.result_highlight.positionedOffset().top,b=c+this.result_highlight.getHeight();if(b>=e)return this.search_results.scrollTop=b-d>0?b-d:0;if(c<f)return this.search_results.scrollTop=c},a.prototype.result_clear_highlight=function(){return this.result_highlight&&this.result_highlight.removeClassName("highlighted"),this.result_highlight=null},a.prototype.results_show=function(){var a;return this.is_multiple||(this.selected_item.addClassName("chzn-single-with-drop"),this.result_single_selected&&this.result_do_highlight(this.result_single_selected)),a=this.is_multiple?this.container.getHeight():this.container.getHeight()-1,this.dropdown.setStyle({top:a+"px",left:0}),this.results_showing=!0,this.search_field.focus(),this.search_field.value=this.search_field.value,this.winnow_results()},a.prototype.results_hide=function(){return this.is_multiple||this.selected_item.removeClassName("chzn-single-with-drop"),this.result_clear_highlight(),this.dropdown.setStyle({left:"-9000px"}),this.results_showing=!1},a.prototype.set_tab_index=function(a){var b;if(this.form_field.tabIndex)return b=this.form_field.tabIndex,this.form_field.tabIndex=-1,this.is_multiple?this.search_field.tabIndex=b:(this.selected_item.tabIndex=b,this.search_field.tabIndex=-1)},a.prototype.show_search_field_default=function(){return this.is_multiple&&this.choices<1&&!this.active_field?(this.search_field.value=this.default_text,this.search_field.addClassName("default")):(this.search_field.value="",this.search_field.removeClassName("default"))},a.prototype.search_results_mouseup=function(a){var b;b=a.target.hasClassName("active-result")?a.target:a.target.up(".active-result");if(b)return this.result_highlight=b,this.result_select(a)},a.prototype.search_results_mouseover=function(a){var b;b=a.target.hasClassName("active-result")?a.target:a.target.up(".active-result");if(b)return this.result_do_highlight(b)},a.prototype.search_results_mouseout=function(a){if(a.target.hasClassName("active-result")||a.target.up(".active-result"))return this.result_clear_highlight()},a.prototype.choices_click=function(a){a.preventDefault();if(this.active_field&&!a.target.hasClassName("search-choice")&&!a.target.up(".search-choice")&&!this.results_showing)return this.results_show()},a.prototype.choice_build=function(a){var b,c;return b=this.container_id+"_c_"+a.array_index,this.choices+=1,this.search_container.insert({before:this.choice_temp.evaluate({id:b,choice:a.html,position:a.array_index})}),c=$(b).down("a"),c.observe("click",f(function(a){return this.choice_destroy_link_click(a)},this))},a.prototype.choice_destroy_link_click=function(a){a.preventDefault();if(!this.is_disabled)return this.pending_destroy_click=!0,this.choice_destroy(a.target)},a.prototype.choice_destroy=function(a){return this.choices-=1,this.show_search_field_default(),this.is_multiple&&this.choices>0&&this.search_field.value.length<1&&this.results_hide(),this.result_deselect(a.readAttribute("rel")),a.up("li").remove()},a.prototype.results_reset=function(a){this.form_field.options[0].selected=!0,this.selected_item.down("span").update(this.default_text),this.show_search_field_default(),a.target.remove(),typeof Event.simulate=="function"&&this.form_field.simulate("change");if(this.active_field)return this.results_hide()},a.prototype.result_select=function(a){var b,c,d;if(this.result_highlight)return b=this.result_highlight,this.result_clear_highlight(),this.is_multiple?this.result_deactivate(b):(this.search_results.descendants(".result-selected").invoke("removeClassName","result-selected"),this.result_single_selected=b),b.addClassName("result-selected"),d=b.id.substr(b.id.lastIndexOf("_")+1),c=this.results_data[d],c.selected=!0,this.form_field.options[c.options_index].selected=!0,this.is_multiple?this.choice_build(c):(this.selected_item.down("span").update(c.html),this.allow_single_deselect&&this.single_deselect_control_build()),(!a.metaKey||!this.is_multiple)&&this.results_hide(),this.search_field.value="",typeof Event.simulate=="function"&&this.form_field.simulate("change"),this.search_field_scale()},a.prototype.result_activate=function(a){return a.addClassName("active-result")},a.prototype.result_deactivate=function(a){return a.removeClassName("active-result")},a.prototype.result_deselect=function(a){var b,c;return c=this.results_data[a],c.selected=!1,this.form_field.options[c.options_index].selected=!1,b=$(this.container_id+"_o_"+a),b.removeClassName("result-selected").addClassName("active-result").show(),this.result_clear_highlight(),this.winnow_results(),typeof Event.simulate=="function"&&this.form_field.simulate("change"),this.search_field_scale()},a.prototype.single_deselect_control_build=function(){if(this.allow_single_deselect&&!this.selected_item.down("abbr"))return this.selected_item.down("span").insert({after:'<abbr class="search-choice-close"></abbr>'})},a.prototype.winnow_results=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p;this.no_results_clear(),g=0,h=this.search_field.value===this.default_text?"":this.search_field.value.strip().escapeHTML(),e=new RegExp("^"+h.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i"),k=new RegExp(h.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i"),p=this.results_data;for(l=0,n=p.length;l<n;l++){b=p[l];if(!b.disabled&&!b.empty)if(b.group)$(b.dom_id).hide();else if(!this.is_multiple||!b.selected){a=!1,f=b.dom_id;if(e.test(b.html))a=!0,g+=1;else if(b.html.indexOf(" ")>=0||b.html.indexOf("[")===0){d=b.html.replace(/\[|\]/g,"").split(" ");if(d.length)for(m=0,o=d.length;m<o;m++)c=d[m],e.test(c)&&(a=!0,g+=1)}a?(h.length?(i=b.html.search(k),j=b.html.substr(0,i+h.length)+"</em>"+b.html.substr(i+h.length),j=j.substr(0,i)+"<em>"+j.substr(i)):j=b.html,$(f).innerHTML!==j&&$(f).update(j),this.result_activate($(f)),b.group_array_index!=null&&$(this.results_data[b.group_array_index].dom_id).setStyle({display:"list-item"})):($(f)===this.result_highlight&&this.result_clear_highlight(),this.result_deactivate($(f)))}}return g<1&&h.length?this.no_results(h):this.winnow_results_set_highlight()},a.prototype.winnow_results_clear=function(){var a,b,c,d,e;this.search_field.clear(),b=this.search_results.select("li"),e=[];for(c=0,d=b.length;c<d;c++)a=b[c],e.push(a.hasClassName("group-result")?a.show():!this.is_multiple||!a.hasClassName("result-selected")?this.result_activate(a):void 0);return e},a.prototype.winnow_results_set_highlight=function(){var a;if(!this.result_highlight){this.is_multiple||(a=this.search_results.down(".result-selected.active-result")),a==null&&(a=this.search_results.down(".active-result"));if(a!=null)return this.result_do_highlight(a)}},a.prototype.no_results=function(a){return this.search_results.insert(this.no_results_temp.evaluate({terms:a}))},a.prototype.no_results_clear=function(){var a,b;a=null,b=[];while(a=this.search_results.down(".no-results"))b.push(a.remove());return b},a.prototype.keydown_arrow=function(){var a,b,c;a=this.search_results.select("li.active-result");if(a.length){this.result_highlight?this.results_showing&&(c=this.result_highlight.nextSiblings(),b=c.intersect(a),b.length&&this.result_do_highlight(b.first())):this.result_do_highlight(a.first());if(!this.results_showing)return this.results_show()}},a.prototype.keyup_arrow=function(){var a,b,c;if(!this.results_showing&&!this.is_multiple)return this.results_show();if(this.result_highlight)return c=this.result_highlight.previousSiblings(),a=this.search_results.select("li.active-result"),b=c.intersect(a),b.length?this.result_do_highlight(b.first()):(this.choices>0&&this.results_hide(),this.result_clear_highlight())},a.prototype.keydown_backstroke=function(){return this.pending_backstroke?(this.choice_destroy(this.pending_backstroke.down("a")),this.clear_backstroke()):(this.pending_backstroke=this.search_container.siblings("li.search-choice").last(),this.pending_backstroke.addClassName("search-choice-focus"))},a.prototype.clear_backstroke=function(){return this.pending_backstroke&&this.pending_backstroke.removeClassName("search-choice-focus"),this.pending_backstroke=null},a.prototype.keydown_checker=function(a){var b,c;b=(c=a.which)!=null?c:a.keyCode,this.search_field_scale(),b!==8&&this.pending_backstroke&&this.clear_backstroke();switch(b){case 8:this.backstroke_length=this.search_field.value.length;break;case 9:this.results_showing&&!this.is_multiple&&this.result_select(a),this.mouse_on_container=!1;break;case 13:a.preventDefault();break;case 38:a.preventDefault(),this.keyup_arrow();break;case 40:this.keydown_arrow()}},a.prototype.search_field_scale=function(){var a,b,c,d,e,f,g,h,i;if(this.is_multiple){c=0,g=0,e="position:absolute; left: -1000px; top: -1000px; display:none;",f=["font-size","font-style","font-weight","font-family","line-height","text-transform","letter-spacing"];for(h=0,i=f.length;h<i;h++)d=f[h],e+=d+":"+this.search_field.getStyle(d)+";";return b=(new Element("div",{style:e})).update(this.search_field.value.escapeHTML()),document.body.appendChild(b),g=Element.measure(b,"width")+25,b.remove(),g>this.f_width-10&&(g=this.f_width-10),this.search_field.setStyle({width:g+"px"}),a=this.container.getHeight(),this.dropdown.setStyle({top:a+"px"})}},a}(),c.Chosen=a,Prototype.Browser.IE&&/MSIE (\d+\.\d+);/.test(navigator.userAgent)&&(Prototype.BrowserFeatures.Version=new Number(RegExp.$1)),b=function(a){var b,c;return b=new Element.Layout(a),c=b.get("border-left")+b.get("border-right")+b.get("padding-left")+b.get("padding-right")},c.get_side_border_padding=b}.call(this) ((function(){var a;a=function(){function a(){this.options_index=0,this.parsed=[]}return a.prototype.add_node=function(a){return a.nodeName==="OPTGROUP"?this.add_group(a):this.add_option(a)},a.prototype.add_group=function(a){var b,c,d,e,f,g;b=this.parsed.length,this.parsed.push({array_index:b,group:!0,label:a.label,children:0,disabled:a.disabled}),f=a.childNodes,g=[];for(d=0,e=f.length;d<e;d++)c=f[d],g.push(this.add_option(c,b,a.disabled));return g},a.prototype.add_option=function(a,b,c){if(a.nodeName==="OPTION")return a.text!==""?(b!=null&&(this.parsed[b].children+=1),this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:a.value,text:a.text,html:a.innerHTML,selected:a.selected,disabled:c===!0?c:a.disabled,group_array_index:b,classes:a.className,style:a.style.cssText})):this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:!0}),this.options_index+=1},a}(),a.select_to_array=function(b){var c,d,e,f,g;d=new a,g=b.childNodes;for(e=0,f=g.length;e<f;e++)c=g[e],d.add_node(c);return d.parsed},this.SelectParser=a})).call(this),function(){var a,b;b=this,a=function(){function a(a,b){this.form_field=a,this.options=b!=null?b:{},this.set_default_values(),this.is_multiple=this.form_field.multiple,this.default_text_default=this.is_multiple?"Select Some Options":"Select an Option",this.setup(),this.set_up_html(),this.register_observers(),this.finish_setup()}return a.prototype.set_default_values=function(){var a=this;return this.click_test_action=function(b){return a.test_active_click(b)},this.activate_action=function(b){return a.activate_field(b)},this.active_field=!1,this.mouse_on_container=!1,this.results_showing=!1,this.result_highlighted=null,this.result_single_selected=null,this.allow_single_deselect=this.options.allow_single_deselect!=null&&this.form_field.options[0]!=null&&this.form_field.options[0].text===""?this.options.allow_single_deselect:!1,this.disable_search_threshold=this.options.disable_search_threshold||0,this.choices=0,this.results_none_found=this.options.no_results_text||"No results match"},a.prototype.mouse_enter=function(){return this.mouse_on_container=!0},a.prototype.mouse_leave=function(){return this.mouse_on_container=!1},a.prototype.input_focus=function(a){var b=this;if(!this.active_field)return setTimeout(function(){return b.container_mousedown()},50)},a.prototype.input_blur=function(a){var b=this;if(!this.mouse_on_container)return this.active_field=!1,setTimeout(function(){return b.blur_test()},100)},a.prototype.result_add_option=function(a){var b,c;return a.disabled?"":(a.dom_id=this.container_id+"_o_"+a.array_index,b=a.selected&&this.is_multiple?[]:["active-result"],a.selected&&b.push("result-selected"),a.group_array_index!=null&&b.push("group-option"),a.classes!==""&&b.push(a.classes),c=a.style.cssText!==""?' style="'+a.style+'"':"",'<li id="'+a.dom_id+'" class="'+b.join(" ")+'"'+c+">"+a.html+"</li>")},a.prototype.results_update_field=function(){return this.result_clear_highlight(),this.result_single_selected=null,this.results_build()},a.prototype.results_toggle=function(){return this.results_showing?this.results_hide():this.results_show()},a.prototype.results_search=function(a){return this.results_showing?this.winnow_results():this.results_show()},a.prototype.keyup_checker=function(a){var b,c;b=(c=a.which)!=null?c:a.keyCode,this.search_field_scale();switch(b){case 8:if(this.is_multiple&&this.backstroke_length<1&&this.choices>0)return this.keydown_backstroke();if(!this.pending_backstroke)return this.result_clear_highlight(),this.results_search();break;case 13:a.preventDefault();if(this.results_showing)return this.result_select(a);break;case 27:return this.results_showing&&this.results_hide(),!0;case 9:case 38:case 40:case 16:case 91:case 17:break;default:return this.results_search()}},a.prototype.generate_field_id=function(){var a;return a=this.generate_random_id(),this.form_field.id=a,a},a.prototype.generate_random_char=function(){var a,b,c;return a="0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZ",c=Math.floor(Math.random()*a.length),b=a.substring(c,c+1)},a}(),b.AbstractChosen=a}.call(this),function(){var a,b,c,d=Object.prototype.hasOwnProperty,e=function(a,b){function e(){this.constructor=a}for(var c in b)d.call(b,c)&&(a[c]=b[c]);return e.prototype=b.prototype,a.prototype=new e,a.__super__=b.prototype,a};c=this,a=function(a){function d(){d.__super__.constructor.apply(this,arguments)}return e(d,a),d.prototype.setup=function(){return this.is_rtl=this.form_field.hasClassName("chzn-rtl")},d.prototype.finish_setup=function(){return this.form_field.addClassName("chzn-done")},d.prototype.set_default_values=function(){return d.__super__.set_default_values.call(this),this.single_temp=new Template('<a href="javascript:void(0)" class="chzn-single"><span>#{default}</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" autocomplete="off" /></div><ul class="chzn-results"></ul></div>'),this.multi_temp=new Template('<ul class="chzn-choices"><li class="search-field"><input type="text" value="#{default}" class="default" autocomplete="off" style="width:25px;" /></li></ul><div class="chzn-drop" style="left:-9000px;"><ul class="chzn-results"></ul></div>'),this.choice_temp=new Template('<li class="search-choice" id="#{id}"><span>#{choice}</span><a href="javascript:void(0)" class="search-choice-close" rel="#{position}"></a></li>'),this.no_results_temp=new Template('<li class="no-results">'+this.results_none_found+' "<span>#{terms}</span>"</li>')},d.prototype.set_up_html=function(){var a,c,d,e,f;return this.container_id=this.form_field.identify().replace(/(:|\.)/g,"_")+"_chzn",this.f_width=this.form_field.getStyle("width")?parseInt(this.form_field.getStyle("width"),10):this.form_field.getWidth(),c={id:this.container_id,"class":"chzn-container"+(this.is_rtl?" chzn-rtl":""),style:"width: "+this.f_width+"px"},this.default_text=this.form_field.readAttribute("data-placeholder")?this.form_field.readAttribute("data-placeholder"):this.default_text_default,a=this.is_multiple?(new Element("div",c)).update(this.multi_temp.evaluate({"default":this.default_text})):(new Element("div",c)).update(this.single_temp.evaluate({"default":this.default_text})),this.form_field.hide().insert({after:a}),this.container=$(this.container_id),this.container.addClassName("chzn-container-"+(this.is_multiple?"multi":"single")),this.dropdown=this.container.down("div.chzn-drop"),d=this.container.getHeight(),e=this.f_width-b(this.dropdown),this.dropdown.setStyle({width:e+"px",top:d+"px"}),this.search_field=this.container.down("input"),this.search_results=this.container.down("ul.chzn-results"),this.search_field_scale(),this.search_no_results=this.container.down("li.no-results"),this.is_multiple?(this.search_choices=this.container.down("ul.chzn-choices"),this.search_container=this.container.down("li.search-field")):(this.search_container=this.container.down("div.chzn-search"),this.selected_item=this.container.down(".chzn-single"),f=e-b(this.search_container)-b(this.search_field),this.search_field.setStyle({width:f+"px"})),this.results_build(),this.set_tab_index(),this.form_field.fire("liszt:ready",{chosen:this})},d.prototype.register_observers=function(){var a=this;return this.container.observe("mousedown",function(b){return a.container_mousedown(b)}),this.container.observe("mouseup",function(b){return a.container_mouseup(b)}),this.container.observe("mouseenter",function(b){return a.mouse_enter(b)}),this.container.observe("mouseleave",function(b){return a.mouse_leave(b)}),this.search_results.observe("mouseup",function(b){return a.search_results_mouseup(b)}),this.search_results.observe("mouseover",function(b){return a.search_results_mouseover(b)}),this.search_results.observe("mouseout",function(b){return a.search_results_mouseout(b)}),this.form_field.observe("liszt:updated",function(b){return a.results_update_field(b)}),this.search_field.observe("blur",function(b){return a.input_blur(b)}),this.search_field.observe("keyup",function(b){return a.keyup_checker(b)}),this.search_field.observe("keydown",function(b){return a.keydown_checker(b)}),this.is_multiple?(this.search_choices.observe("click",function(b){return a.choices_click(b)}),this.search_field.observe("focus",function(b){return a.input_focus(b)})):this.container.observe("click",function(a){return a.preventDefault()})},d.prototype.search_field_disabled=function(){this.is_disabled=this.form_field.disabled;if(this.is_disabled)return this.container.addClassName("chzn-disabled"),this.search_field.disabled=!0,this.is_multiple||this.selected_item.stopObserving("focus",this.activate_action),this.close_field();this.container.removeClassName("chzn-disabled"),this.search_field.disabled=!1;if(!this.is_multiple)return this.selected_item.observe("focus",this.activate_action)},d.prototype.container_mousedown=function(a){var b;if(!this.is_disabled)return b=a!=null?a.target.hasClassName("search-choice-close"):!1,a&&a.type==="mousedown"&&a.stop(),!this.pending_destroy_click&&!b?(this.active_field?!this.is_multiple&&a&&(a.target===this.selected_item||a.target.up("a.chzn-single"))&&this.results_toggle():(this.is_multiple&&this.search_field.clear(),document.observe("click",this.click_test_action),this.results_show()),this.activate_field()):this.pending_destroy_click=!1},d.prototype.container_mouseup=function(a){if(a.target.nodeName==="ABBR")return this.results_reset(a)},d.prototype.blur_test=function(a){if(!this.active_field&&this.container.hasClassName("chzn-container-active"))return this.close_field()},d.prototype.close_field=function(){return document.stopObserving("click",this.click_test_action),this.is_multiple||(this.selected_item.tabIndex=this.search_field.tabIndex,this.search_field.tabIndex=-1),this.active_field=!1,this.results_hide(),this.container.removeClassName("chzn-container-active"),this.winnow_results_clear(),this.clear_backstroke(),this.show_search_field_default(),this.search_field_scale()},d.prototype.activate_field=function(){return!this.is_multiple&&!this.active_field&&(this.search_field.tabIndex=this.selected_item.tabIndex,this.selected_item.tabIndex=-1),this.container.addClassName("chzn-container-active"),this.active_field=!0,this.search_field.value=this.search_field.value,this.search_field.focus()},d.prototype.test_active_click=function(a){return a.target.up("#"+this.container_id)?this.active_field=!0:this.close_field()},d.prototype.results_build=function(){var a,b,d,e,f;this.parsing=!0,this.results_data=c.SelectParser.select_to_array(this.form_field),this.is_multiple&&this.choices>0?(this.search_choices.select("li.search-choice").invoke("remove"),this.choices=0):this.is_multiple||(this.selected_item.down("span").update(this.default_text),this.form_field.options.length<=this.disable_search_threshold?this.container.addClassName("chzn-container-single-nosearch"):this.container.removeClassName("chzn-container-single-nosearch")),a="",f=this.results_data;for(d=0,e=f.length;d<e;d++)b=f[d],b.group?a+=this.result_add_group(b):b.empty||(a+=this.result_add_option(b),b.selected&&this.is_multiple?this.choice_build(b):b.selected&&!this.is_multiple&&(this.selected_item.down("span").update(b.html),this.allow_single_deselect&&this.single_deselect_control_build()));return this.search_field_disabled(),this.show_search_field_default(),this.search_field_scale(),this.search_results.update(a),this.parsing=!1},d.prototype.result_add_group=function(a){return a.disabled?"":(a.dom_id=this.container_id+"_g_"+a.array_index,'<li id="'+a.dom_id+'" class="group-result">'+a.label.escapeHTML()+"</li>")},d.prototype.result_do_highlight=function(a){var b,c,d,e,f;this.result_clear_highlight(),this.result_highlight=a,this.result_highlight.addClassName("highlighted"),d=parseInt(this.search_results.getStyle("maxHeight"),10),f=this.search_results.scrollTop,e=d+f,c=this.result_highlight.positionedOffset().top,b=c+this.result_highlight.getHeight();if(b>=e)return this.search_results.scrollTop=b-d>0?b-d:0;if(c<f)return this.search_results.scrollTop=c},d.prototype.result_clear_highlight=function(){return this.result_highlight&&this.result_highlight.removeClassName("highlighted"),this.result_highlight=null},d.prototype.results_show=function(){var a;return this.is_multiple||(this.selected_item.addClassName("chzn-single-with-drop"),this.result_single_selected&&this.result_do_highlight(this.result_single_selected)),a=this.is_multiple?this.container.getHeight():this.container.getHeight()-1,this.dropdown.setStyle({top:a+"px",left:0}),this.results_showing=!0,this.search_field.focus(),this.search_field.value=this.search_field.value,this.winnow_results()},d.prototype.results_hide=function(){return this.is_multiple||this.selected_item.removeClassName("chzn-single-with-drop"),this.result_clear_highlight(),this.dropdown.setStyle({left:"-9000px"}),this.results_showing=!1},d.prototype.set_tab_index=function(a){var b;if(this.form_field.tabIndex)return b=this.form_field.tabIndex,this.form_field.tabIndex=-1,this.is_multiple?this.search_field.tabIndex=b:(this.selected_item.tabIndex=b,this.search_field.tabIndex=-1)},d.prototype.show_search_field_default=function(){return this.is_multiple&&this.choices<1&&!this.active_field?(this.search_field.value=this.default_text,this.search_field.addClassName("default")):(this.search_field.value="",this.search_field.removeClassName("default"))},d.prototype.search_results_mouseup=function(a){var b;b=a.target.hasClassName("active-result")?a.target:a.target.up(".active-result");if(b)return this.result_highlight=b,this.result_select(a)},d.prototype.search_results_mouseover=function(a){var b;b=a.target.hasClassName("active-result")?a.target:a.target.up(".active-result");if(b)return this.result_do_highlight(b)},d.prototype.search_results_mouseout=function(a){if(a.target.hasClassName("active-result")||a.target.up(".active-result"))return this.result_clear_highlight()},d.prototype.choices_click=function(a){a.preventDefault();if(this.active_field&&!a.target.hasClassName("search-choice")&&!a.target.up(".search-choice")&&!this.results_showing)return this.results_show()},d.prototype.choice_build=function(a){var b,c,d=this;return b=this.container_id+"_c_"+a.array_index,this.choices+=1,this.search_container.insert({before:this.choice_temp.evaluate({id:b,choice:a.html,position:a.array_index})}),c=$(b).down("a"),c.observe("click",function(a){return d.choice_destroy_link_click(a)})},d.prototype.choice_destroy_link_click=function(a){a.preventDefault();if(!this.is_disabled)return this.pending_destroy_click=!0,this.choice_destroy(a.target)},d.prototype.choice_destroy=function(a){return this.choices-=1,this.show_search_field_default(),this.is_multiple&&this.choices>0&&this.search_field.value.length<1&&this.results_hide(),this.result_deselect(a.readAttribute("rel")),a.up("li").remove()},d.prototype.results_reset=function(a){this.form_field.options[0].selected=!0,this.selected_item.down("span").update(this.default_text),this.show_search_field_default(),a.target.remove(),typeof Event.simulate=="function"&&this.form_field.simulate("change");if(this.active_field)return this.results_hide()},d.prototype.result_select=function(a){var b,c,d;if(this.result_highlight)return b=this.result_highlight,this.result_clear_highlight(),this.is_multiple?this.result_deactivate(b):(this.search_results.descendants(".result-selected").invoke("removeClassName","result-selected"),this.result_single_selected=b),b.addClassName("result-selected"),d=b.id.substr(b.id.lastIndexOf("_")+1),c=this.results_data[d],c.selected=!0,this.form_field.options[c.options_index].selected=!0,this.is_multiple?this.choice_build(c):(this.selected_item.down("span").update(c.html),this.allow_single_deselect&&this.single_deselect_control_build()),(!a.metaKey||!this.is_multiple)&&this.results_hide(),this.search_field.value="",typeof Event.simulate=="function"&&this.form_field.simulate("change"),this.search_field_scale()},d.prototype.result_activate=function(a){return a.addClassName("active-result")},d.prototype.result_deactivate=function(a){return a.removeClassName("active-result")},d.prototype.result_deselect=function(a){var b,c;return c=this.results_data[a],c.selected=!1,this.form_field.options[c.options_index].selected=!1,b=$(this.container_id+"_o_"+a),b.removeClassName("result-selected").addClassName("active-result").show(),this.result_clear_highlight(),this.winnow_results(),typeof Event.simulate=="function"&&this.form_field.simulate("change"),this.search_field_scale()},d.prototype.single_deselect_control_build=function(){if(this.allow_single_deselect&&!this.selected_item.down("abbr"))return this.selected_item.down("span").insert({after:'<abbr class="search-choice-close"></abbr>'})},d.prototype.winnow_results=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p;this.no_results_clear(),g=0,h=this.search_field.value===this.default_text?"":this.search_field.value.strip().escapeHTML(),e=new RegExp("^"+h.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i"),k=new RegExp(h.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i"),p=this.results_data;for(l=0,n=p.length;l<n;l++){b=p[l];if(!b.disabled&&!b.empty)if(b.group)$(b.dom_id).hide();else if(!this.is_multiple||!b.selected){a=!1,f=b.dom_id;if(e.test(b.html))a=!0,g+=1;else if(b.html.indexOf(" ")>=0||b.html.indexOf("[")===0){d=b.html.replace(/\[|\]/g,"").split(" ");if(d.length)for(m=0,o=d.length;m<o;m++)c=d[m],e.test(c)&&(a=!0,g+=1)}a?(h.length?(i=b.html.search(k),j=b.html.substr(0,i+h.length)+"</em>"+b.html.substr(i+h.length),j=j.substr(0,i)+"<em>"+j.substr(i)):j=b.html,$(f).innerHTML!==j&&$(f).update(j),this.result_activate($(f)),b.group_array_index!=null&&$(this.results_data[b.group_array_index].dom_id).setStyle({display:"list-item"})):($(f)===this.result_highlight&&this.result_clear_highlight(),this.result_deactivate($(f)))}}return g<1&&h.length?this.no_results(h):this.winnow_results_set_highlight()},d.prototype.winnow_results_clear=function(){var a,b,c,d,e;this.search_field.clear(),b=this.search_results.select("li"),e=[];for(c=0,d=b.length;c<d;c++)a=b[c],a.hasClassName("group-result")?e.push(a.show()):!this.is_multiple||!a.hasClassName("result-selected")?e.push(this.result_activate(a)):e.push(void 0);return e},d.prototype.winnow_results_set_highlight=function(){var a;if(!this.result_highlight){this.is_multiple||(a=this.search_results.down(".result-selected.active-result")),a==null&&(a=this.search_results.down(".active-result"));if(a!=null)return this.result_do_highlight(a)}},d.prototype.no_results=function(a){return this.search_results.insert(this.no_results_temp.evaluate({terms:a}))},d.prototype.no_results_clear=function(){var a,b;a=null,b=[];while(a=this.search_results.down(".no-results"))b.push(a.remove());return b},d.prototype.keydown_arrow=function(){var a,b,c;a=this.search_results.select("li.active-result");if(a.length){this.result_highlight?this.results_showing&&(c=this.result_highlight.nextSiblings(),b=c.intersect(a),b.length&&this.result_do_highlight(b.first())):this.result_do_highlight(a.first());if(!this.results_showing)return this.results_show()}},d.prototype.keyup_arrow=function(){var a,b,c;if(!this.results_showing&&!this.is_multiple)return this.results_show();if(this.result_highlight)return c=this.result_highlight.previousSiblings(),a=this.search_results.select("li.active-result"),b=c.intersect(a),b.length?this.result_do_highlight(b.first()):(this.choices>0&&this.results_hide(),this.result_clear_highlight())},d.prototype.keydown_backstroke=function(){return this.pending_backstroke?(this.choice_destroy(this.pending_backstroke.down("a")),this.clear_backstroke()):(this.pending_backstroke=this.search_container.siblings("li.search-choice").last(),this.pending_backstroke.addClassName("search-choice-focus"))},d.prototype.clear_backstroke=function(){return this.pending_backstroke&&this.pending_backstroke.removeClassName("search-choice-focus"),this.pending_backstroke=null},d.prototype.keydown_checker=function(a){var b,c;b=(c=a.which)!=null?c:a.keyCode,this.search_field_scale(),b!==8&&this.pending_backstroke&&this.clear_backstroke();switch(b){case 8:this.backstroke_length=this.search_field.value.length;break;case 9:this.results_showing&&!this.is_multiple&&this.result_select(a),this.mouse_on_container=!1;break;case 13:a.preventDefault();break;case 38:a.preventDefault(),this.keyup_arrow();break;case 40:this.keydown_arrow()}},d.prototype.search_field_scale=function(){var a,b,c,d,e,f,g,h,i;if(this.is_multiple){c=0,g=0,e="position:absolute; left: -1000px; top: -1000px; display:none;",f=["font-size","font-style","font-weight","font-family","line-height","text-transform","letter-spacing"];for(h=0,i=f.length;h<i;h++)d=f[h],e+=d+":"+this.search_field.getStyle(d)+";";return b=(new Element("div",{style:e})).update(this.search_field.value.escapeHTML()),document.body.appendChild(b),g=Element.measure(b,"width")+25,b.remove(),g>this.f_width-10&&(g=this.f_width-10),this.search_field.setStyle({width:g+"px"}),a=this.container.getHeight(),this.dropdown.setStyle({top:a+"px"})}},d}(AbstractChosen),c.Chosen=a,Prototype.Browser.IE&&/MSIE (\d+\.\d+);/.test(navigator.userAgent)&&(Prototype.BrowserFeatures.Version=new Number(RegExp.$1)),b=function(a){var b,c;return b=new Element.Layout(a),c=b.get("border-left")+b.get("border-right")+b.get("padding-left")+b.get("padding-right")},c.get_side_border_padding=b}.call(this)

View File

@ -90,6 +90,8 @@ class Chosen extends AbstractChosen
if @is_multiple if @is_multiple
@search_choices.click (evt) => this.choices_click(evt) @search_choices.click (evt) => this.choices_click(evt)
@search_field.focus (evt) => this.input_focus(evt) @search_field.focus (evt) => this.input_focus(evt)
else
@container.click (evt) => evt.preventDefault() # gobble click of anchor
search_field_disabled: -> search_field_disabled: ->
@is_disabled = @form_field_jq[0].disabled @is_disabled = @form_field_jq[0].disabled

View File

@ -83,6 +83,8 @@ class Chosen extends AbstractChosen
if @is_multiple if @is_multiple
@search_choices.observe "click", (evt) => this.choices_click(evt) @search_choices.observe "click", (evt) => this.choices_click(evt)
@search_field.observe "focus", (evt) => this.input_focus(evt) @search_field.observe "focus", (evt) => this.input_focus(evt)
else
@container.observe "click", (evt) => evt.preventDefault() # gobble click of anchor
search_field_disabled: -> search_field_disabled: ->
@is_disabled = @form_field.disabled @is_disabled = @form_field.disabled

View File

@ -91,6 +91,7 @@ class AbstractChosen
this.result_select(evt) if this.results_showing this.result_select(evt) if this.results_showing
when 27 when 27
this.results_hide() if @results_showing this.results_hide() if @results_showing
return true
when 9, 38, 40, 16, 91, 17 when 9, 38, 40, 16, 91, 17
# don't do anything on these keys # don't do anything on these keys
else this.results_search() else this.results_search()

View File

@ -54,13 +54,12 @@
<h1>Chosen</h1> <h1>Chosen</h1>
<p>Chosen is a JavaScript plugin for jQuery and Prototype that makes long, unwieldy select boxes much more user-friendly. For more information (including usage, explanation and faqs), check out the <a href="http://harvesthq.github.com/chosen/">online documentation</a>.</p> <p>Chosen is a JavaScript plugin for jQuery and Prototype that makes long, unwieldy select boxes much more user-friendly. For more information (including usage, explanation and faqs), check out the <a href="http://harvesthq.github.com/chosen/">online documentation</a>.</p>
<h2>Standard Select</h2> <h2>Multiple Select</h2>
<div class="side-by-side clearfix"> <div class="side-by-side clearfix">
<div> <div>
<em>Turns This</em> <em>Turns This</em>
<select data-placeholder="Choose a Country..." style="width:350px;" tabindex="1"> <select data-placeholder="Choose a Country..." style="width:350px;" multiple tabindex="3">
<option value=""></option> <option value="United States" disabled="disabled" selected="selected">United States</option>
<option value="United States">United States</option>
<option value="United Kingdom">United Kingdom</option> <option value="United Kingdom">United Kingdom</option>
<option value="Afghanistan">Afghanistan</option> <option value="Afghanistan">Afghanistan</option>
<option value="Albania">Albania</option> <option value="Albania">Albania</option>
@ -307,11 +306,11 @@
</div> </div>
<div> <div>
<em>Into This</em> <em>Into This</em>
<select data-placeholder="Choose a Country..." class="chzn-select" style="width:350px;" tabindex="2"> <select data-placeholder="Choose a Country..." class="chzn-select" multiple style="width:350px;" tabindex="4">
<option value=""></option> <option value=""></option>
<option value="United States">United States</option> <option value="United States" disabled="disabled" selected="selected">United States</option>
<option value="United Kingdom">United Kingdom</option> <option value="United Kingdom">United Kingdom</option>
<option value="Afghanistan">Afghanistan</option> <option value="Afghanistan" disabled="disabled">Afghanistan</option>
<option value="Albania">Albania</option> <option value="Albania">Albania</option>
<option value="Algeria">Algeria</option> <option value="Algeria">Algeria</option>
<option value="American Samoa">American Samoa</option> <option value="American Samoa">American Samoa</option>
@ -556,212 +555,267 @@
</div> </div>
</div> </div>
<h2>Multiple Select</h2> <h2>&lt;optgroup&gt; Support</h2>
<div class="side-by-side clearfix"> <div class="side-by-side clearfix">
<div> <div>
<em>Turns This</em> <em>Single Select with Groups</em>
<select data-placeholder="Choose a Country..." style="width:350px;" multiple tabindex="3"> <select data-placeholder="Your Favorite Football Team" style="width:350px;" class="chzn-select" tabindex="5">
<option value="United States">United States</option> <option value=""></option>
<option value="United Kingdom">United Kingdom</option> <optgroup label="NFC EAST">
<option value="Afghanistan">Afghanistan</option> <option>Dallas Cowboys</option>
<option value="Albania">Albania</option> <option>New York Giants</option>
<option value="Algeria">Algeria</option> <option>Philadelphia Eagles</option>
<option value="American Samoa">American Samoa</option> <option>Washington Redskins</option>
<option value="Andorra">Andorra</option> </optgroup>
<option value="Angola">Angola</option> <optgroup label="NFC NORTH">
<option value="Anguilla">Anguilla</option> <option>Chicago Bears</option>
<option value="Antarctica">Antarctica</option> <option>Detroit Lions</option>
<option value="Antigua and Barbuda">Antigua and Barbuda</option> <option>Green Bay Packers</option>
<option value="Argentina">Argentina</option> <option>Minnesota Vikings</option>
<option value="Armenia">Armenia</option> </optgroup>
<option value="Aruba">Aruba</option> <optgroup label="NFC SOUTH">
<option value="Australia">Australia</option> <option>Atlanta Falcons</option>
<option value="Austria">Austria</option> <option>Carolina Panthers</option>
<option value="Azerbaijan">Azerbaijan</option> <option>New Orleans Saints</option>
<option value="Bahamas">Bahamas</option> <option>Tampa Bay Buccaneers</option>
<option value="Bahrain">Bahrain</option> </optgroup>
<option value="Bangladesh">Bangladesh</option> <optgroup label="NFC WEST">
<option value="Barbados">Barbados</option> <option>Arizona Cardinals</option>
<option value="Belarus">Belarus</option> <option>St. Louis Rams</option>
<option value="Belgium">Belgium</option> <option>San Francisco 49ers</option>
<option value="Belize">Belize</option> <option>Seattle Seahawks</option>
<option value="Benin">Benin</option> </optgroup>
<option value="Bermuda">Bermuda</option> <optgroup label="AFC EAST">
<option value="Bhutan">Bhutan</option> <option>Buffalo Bills</option>
<option value="Bolivia">Bolivia</option> <option>Miami Dolphins</option>
<option value="Bosnia and Herzegovina">Bosnia and Herzegovina</option> <option>New England Patriots</option>
<option value="Botswana">Botswana</option> <option>New York Jets</option>
<option value="Bouvet Island">Bouvet Island</option> </optgroup>
<option value="Brazil">Brazil</option> <optgroup label="AFC NORTH">
<option value="British Indian Ocean Territory">British Indian Ocean Territory</option> <option>Baltimore Ravens</option>
<option value="Brunei Darussalam">Brunei Darussalam</option> <option>Cincinnati Bengals</option>
<option value="Bulgaria">Bulgaria</option> <option>Cleveland Browns</option>
<option value="Burkina Faso">Burkina Faso</option> <option>Pittsburgh Steelers</option>
<option value="Burundi">Burundi</option> </optgroup>
<option value="Cambodia">Cambodia</option> <optgroup label="AFC SOUTH">
<option value="Cameroon">Cameroon</option> <option>Houston Texans</option>
<option value="Canada">Canada</option> <option>Indianapolis Colts</option>
<option value="Cape Verde">Cape Verde</option> <option>Jacksonville Jaguars</option>
<option value="Cayman Islands">Cayman Islands</option> <option>Tennessee Titans</option>
<option value="Central African Republic">Central African Republic</option> </optgroup>
<option value="Chad">Chad</option> <optgroup label="AFC WEST">
<option value="Chile">Chile</option> <option>Denver Broncos</option>
<option value="China">China</option> <option>Kansas City Chiefs</option>
<option value="Christmas Island">Christmas Island</option> <option>Oakland Raiders</option>
<option value="Cocos (Keeling) Islands">Cocos (Keeling) Islands</option> <option>San Diego Chargers</option>
<option value="Colombia">Colombia</option> </optgroup>
<option value="Comoros">Comoros</option> </select>
<option value="Congo">Congo</option> </div>
<option value="Congo, The Democratic Republic of The">Congo, The Democratic Republic of The</option> <div>
<option value="Cook Islands">Cook Islands</option> <em>Multiple Select with Groups</em>
<option value="Costa Rica">Costa Rica</option> <select data-placeholder="Your Favorite Football Team" style="width:350px;" class="chzn-select" multiple tabindex="6">
<option value="Cote D'ivoire">Cote D'ivoire</option> <option value=""></option>
<option value="Croatia">Croatia</option> <optgroup label="NFC EAST">
<option value="Cuba">Cuba</option> <option>Dallas Cowboys</option>
<option value="Cyprus">Cyprus</option> <option>New York Giants</option>
<option value="Czech Republic">Czech Republic</option> <option>Philadelphia Eagles</option>
<option value="Denmark">Denmark</option> <option>Washington Redskins</option>
<option value="Djibouti">Djibouti</option> </optgroup>
<option value="Dominica">Dominica</option> <optgroup label="NFC NORTH">
<option value="Dominican Republic">Dominican Republic</option> <option>Chicago Bears</option>
<option value="Ecuador">Ecuador</option> <option>Detroit Lions</option>
<option value="Egypt">Egypt</option> <option>Green Bay Packers</option>
<option value="El Salvador">El Salvador</option> <option>Minnesota Vikings</option>
<option value="Equatorial Guinea">Equatorial Guinea</option> </optgroup>
<option value="Eritrea">Eritrea</option> <optgroup label="NFC SOUTH">
<option value="Estonia">Estonia</option> <option>Atlanta Falcons</option>
<option value="Ethiopia">Ethiopia</option> <option>Carolina Panthers</option>
<option value="Falkland Islands (Malvinas)">Falkland Islands (Malvinas)</option> <option>New Orleans Saints</option>
<option value="Faroe Islands">Faroe Islands</option> <option>Tampa Bay Buccaneers</option>
<option value="Fiji">Fiji</option> </optgroup>
<option value="Finland">Finland</option> <optgroup label="NFC WEST">
<option value="France">France</option> <option>Arizona Cardinals</option>
<option value="French Guiana">French Guiana</option> <option>St. Louis Rams</option>
<option value="French Polynesia">French Polynesia</option> <option>San Francisco 49ers</option>
<option value="French Southern Territories">French Southern Territories</option> <option>Seattle Seahawks</option>
<option value="Gabon">Gabon</option> </optgroup>
<option value="Gambia">Gambia</option> <optgroup label="AFC EAST">
<option value="Georgia">Georgia</option> <option>Buffalo Bills</option>
<option value="Germany">Germany</option> <option>Miami Dolphins</option>
<option value="Ghana">Ghana</option> <option>New England Patriots</option>
<option value="Gibraltar">Gibraltar</option> <option>New York Jets</option>
<option value="Greece">Greece</option> </optgroup>
<option value="Greenland">Greenland</option> <optgroup label="AFC NORTH">
<option value="Grenada">Grenada</option> <option>Baltimore Ravens</option>
<option value="Guadeloupe">Guadeloupe</option> <option>Cincinnati Bengals</option>
<option value="Guam">Guam</option> <option>Cleveland Browns</option>
<option value="Guatemala">Guatemala</option> <option>Pittsburgh Steelers</option>
<option value="Guinea">Guinea</option> </optgroup>
<option value="Guinea-bissau">Guinea-bissau</option> <optgroup label="AFC SOUTH">
<option value="Guyana">Guyana</option> <option>Houston Texans</option>
<option value="Haiti">Haiti</option> <option>Indianapolis Colts</option>
<option value="Heard Island and Mcdonald Islands">Heard Island and Mcdonald Islands</option> <option>Jacksonville Jaguars</option>
<option value="Holy See (Vatican City State)">Holy See (Vatican City State)</option> <option>Tennessee Titans</option>
<option value="Honduras">Honduras</option> </optgroup>
<option value="Hong Kong">Hong Kong</option> <optgroup label="AFC WEST">
<option value="Hungary">Hungary</option> <option>Denver Broncos</option>
<option value="Iceland">Iceland</option> <option>Kansas City Chiefs</option>
<option value="India">India</option> <option>Oakland Raiders</option>
<option value="Indonesia">Indonesia</option> <option>San Diego Chargers</option>
<option value="Iran, Islamic Republic of">Iran, Islamic Republic of</option> </optgroup>
<option value="Iraq">Iraq</option> </select>
<option value="Ireland">Ireland</option> </div>
<option value="Israel">Israel</option> </div>
<option value="Italy">Italy</option>
<option value="Jamaica">Jamaica</option> <h2>Selected and Disabled Support</h2>
<option value="Japan">Japan</option> <div class="side-by-side clearfix">
<option value="Jordan">Jordan</option> <p>Chosen automatically highlights selected options and removes disabled options.</p>
<option value="Kazakhstan">Kazakhstan</option> <div>
<option value="Kenya">Kenya</option> <em>Single Select</em>
<option value="Kiribati">Kiribati</option> <select data-placeholder="Your Favorite Type of Bear" style="width:350px;" class="chzn-select" tabindex="7">
<option value="Korea, Democratic People's Republic of">Korea, Democratic People's Republic of</option> <option value=""></option>
<option value="Korea, Republic of">Korea, Republic of</option> <option>American Black Bear</option>
<option value="Kuwait">Kuwait</option> <option>Asiatic Black Bear</option>
<option value="Kyrgyzstan">Kyrgyzstan</option> <option>Brown Bear</option>
<option value="Lao People's Democratic Republic">Lao People's Democratic Republic</option> <option>Giant Panda</option>
<option value="Latvia">Latvia</option> <option selected>Sloth Bear</option>
<option value="Lebanon">Lebanon</option> <option disabled>Sun Bear</option>
<option value="Lesotho">Lesotho</option> <option>Polar Bear</option>
<option value="Liberia">Liberia</option> <option disabled>Spectacled Bear</option>
<option value="Libyan Arab Jamahiriya">Libyan Arab Jamahiriya</option> </select>
<option value="Liechtenstein">Liechtenstein</option> </div>
<option value="Lithuania">Lithuania</option> <div>
<option value="Luxembourg">Luxembourg</option> <em>Multiple Select</em>
<option value="Macao">Macao</option> <select data-placeholder="Your Favorite Types of Bear" style="width:350px;" multiple class="chzn-select" tabindex="8">
<option value="Macedonia, The Former Yugoslav Republic of">Macedonia, The Former Yugoslav Republic of</option> <option value=""></option>
<option value="Madagascar">Madagascar</option> <option>American Black Bear</option>
<option value="Malawi">Malawi</option> <option>Asiatic Black Bear</option>
<option value="Malaysia">Malaysia</option> <option>Brown Bear</option>
<option value="Maldives">Maldives</option> <option>Giant Panda</option>
<option value="Mali">Mali</option> <option selected>Sloth Bear</option>
<option value="Malta">Malta</option> <option disabled>Sun Bear</option>
<option value="Marshall Islands">Marshall Islands</option> <option selected>Polar Bear</option>
<option value="Martinique">Martinique</option> <option disabled>Spectacled Bear</option>
<option value="Mauritania">Mauritania</option> </select>
<option value="Mauritius">Mauritius</option> </div>
<option value="Mayotte">Mayotte</option> </div>
<option value="Mexico">Mexico</option>
<option value="Micronesia, Federated States of">Micronesia, Federated States of</option> <h2>Default Text Support</h2>
<option value="Moldova, Republic of">Moldova, Republic of</option> <div class="side-by-side clearfix">
<option value="Monaco">Monaco</option> <p>Chosen automatically sets the default field text ("Choose a country...") by reading the select element's data-placeholder value. If no data-placeholder value is present, it will default to "Select Some Option" or "Select Some Options" depending on whether the select is single or multiple. You can change these elements in the plugin js file as you see fit.</p>
<option value="Mongolia">Mongolia</option> <code>&lt;select <strong>data-placeholder="Choose a country..."</strong> style="width:350px;" multiple class="chzn-select"&gt;</code>
<option value="Montenegro">Montenegro</option> <p><strong>Note:</strong> on single selects, the first element is assumed to be selected by the browser. To take advantage of the default text support, you will need to include a blank option as the first element of your select list.</p>
<option value="Montserrat">Montserrat</option> </div>
<option value="Morocco">Morocco</option>
<option value="Mozambique">Mozambique</option> <h2>No Results Text Support</h2>
<option value="Myanmar">Myanmar</option> <div class="side-by-side clearfix">
<option value="Namibia">Namibia</option> <p>Setting the "No results" search text is as easy as passing an option when you create Chosen:</p>
<option value="Nauru">Nauru</option> <code>
<option value="Nepal">Nepal</option> New Chosen($("chzn_select_field"),{no_results_text: "No results matched"});
<option value="Netherlands">Netherlands</option> </code>
<option value="Netherlands Antilles">Netherlands Antilles</option> </div>
<option value="New Caledonia">New Caledonia</option>
<option value="New Zealand">New Zealand</option> <h2>Allow Deselect on Single Selects</h2>
<option value="Nicaragua">Nicaragua</option> <div class="side-by-side clearfix">
<option value="Niger">Niger</option> <p>When a single select box isn't a required field, you can set <code>allow_single_deselect: true</code> and Chosen will add a UI element for option deselection. This will only work if the first option has blank text.</p>
<option value="Nigeria">Nigeria</option> <div class="side-by-side clearfix">
<option value="Niue">Niue</option> <select data-placeholder="Your Favorite Type of Bear" style="width:350px;" class="chzn-select-deselect" tabindex="7">
<option value="Norfolk Island">Norfolk Island</option> <option value=""></option>
<option value="Northern Mariana Islands">Northern Mariana Islands</option> <option>American Black Bear</option>
<option value="Norway">Norway</option> <option>Asiatic Black Bear</option>
<option value="Oman">Oman</option> <option>Brown Bear</option>
<option value="Pakistan">Pakistan</option> <option>Giant Panda</option>
<option value="Palau">Palau</option> <option selected>Sloth Bear</option>
<option value="Palestinian Territory, Occupied">Palestinian Territory, Occupied</option> <option>Sun Bear</option>
<option value="Panama">Panama</option> <option>Polar Bear</option>
<option value="Papua New Guinea">Papua New Guinea</option> <option>Spectacled Bear</option>
<option value="Paraguay">Paraguay</option> </select>
<option value="Peru">Peru</option> </div>
<option value="Philippines">Philippines</option> </div>
<option value="Pitcairn">Pitcairn</option>
<option value="Poland">Poland</option> <h2>Right to Left Support</h2>
<option value="Portugal">Portugal</option> <div class="side-by-side clearfix">
<option value="Puerto Rico">Puerto Rico</option> <p>Chosen supports right to left select boxes too. just add <code>"chzn-rtl"</code> in addition to <code>"chzn-select"</code> to your select tags and you are good to go.</p>
<option value="Qatar">Qatar</option> <p><code>&lt;select class="chzn-select <strong>chzn-rtl</strong>"&gt;</code></p>
<option value="Reunion">Reunion</option> <div>
<option value="Romania">Romania</option> <em>Single right to left select</em>
<option value="Russian Federation">Russian Federation</option> <select data-placeholder="Your Favorite Type of Bear" style="width:350px;" class="chzn-select chzn-rtl" tabindex="9">
<option value="Rwanda">Rwanda</option> <option value=""></option>
<option value="Saint Helena">Saint Helena</option> <option>American Black Bear</option>
<option value="Saint Kitts and Nevis">Saint Kitts and Nevis</option> <option>Asiatic Black Bear</option>
<option value="Saint Lucia">Saint Lucia</option> <option>Brown Bear</option>
<option value="Saint Pierre and Miquelon">Saint Pierre and Miquelon</option> <option>Giant Panda</option>
<option value="Saint Vincent and The Grenadines">Saint Vincent and The Grenadines</option> <option selected>Sloth Bear</option>
<option value="Samoa">Samoa</option> <option>Polar Bear</option>
<option value="San Marino">San Marino</option> </select>
<option value="Sao Tome and Principe">Sao Tome and Principe</option> </div>
<option value="Saudi Arabia">Saudi Arabia</option> <div>
<option value="Senegal">Senegal</option> <em>Multiple right to left select</em>
<option value="Serbia">Serbia</option> <select data-placeholder="Your Favorite Types of Bear" style="width:350px;" multiple class="chzn-select chzn-rtl" tabindex="10">
<option value="Seychelles">Seychelles</option> <option value=""></option>
<option value="Sierra Leone">Sierra Leone</option> <option>American Black Bear</option>
<option value="Singapore">Singapore</option> <option>Asiatic Black Bear</option>
<option value="Slovakia">Slovakia</option> <option>Brown Bear</option>
<option value="Slovenia">Slovenia</option> <option>Giant Panda</option>
<option value="Solomon Islands">Solomon Islands</option> <option selected>Sloth Bear</option>
<option value="Somalia">Somalia</option> <option selected>Polar Bear</option>
<option value="South Africa">South Africa</option> </select>
<option value="South Georgia and The South Sandwich Islands">South Georgia and The South Sandwich Islands</option> </div>
</div>
<h2>Change / Update Events</h2>
<div class="side-by-side clearfix">
<ul>
<li>
<h3>Form Field Change</h3>
<p>When working with form fields, you often want to perform some behavior after a value has been selected or deselected. Whenever a user selects a field in Chosen, it triggers a "change" event* on the original form field. That let's you do something like this:</p>
<p><code>$("#form_field").chosen().change( &hellip; );</code></p>
<p><strong>Note:</strong> Prototype doesn't offer support for triggering standard browser events. <a href="https://github.com/kangax/protolicious/blob/5b56fdafcd7d7662c9d648534225039b2e78e371/event.simulate.js">Event.simulate</a> is required to trigger the change event when using the Prototype version.</p>
</li>
<li>
<h3>Updating Chosen Dynamically</h3>
<p>If you need to update the options in your select field and want Chosen to pick up the changes, you'll need to trigger the "liszt:updated" event on the field. Chosen will re-build itself based on the updated content.</p>
<ul>
<li><strong>jQuery Version:</strong> <code>$("#form_field").trigger("liszt:updated");</code></li>
<li><strong>Prototype Version:</strong> <code>Event.fire($("form_field"), "liszt:updated");</code></li>
</ul>
</li>
</ul>
</div>
<h2>Setup (for Prototype)</h2>
<p>Using Chosen is easy as can be.</p>
<ol>
<li>Download the plugin and copy the chosen files to your app.</li>
<li>Activate the plugin by creating a new instance of Chosen: new Chosen(<em>some_form_field</em>);</li>
<li><a href="http://youtu.be/pS-RsIzb78U?t=57s">Disco</a>.</li>
</ol>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/prototype/1.7.0.0/prototype.js" type="text/javascript"></script>
<script src="chosen/chosen.proto.js" type="text/javascript"></script>
<script type="text/javascript">
document.observe('dom:loaded', function(evt) {
var select, selects, _i, _len, _results;
if (Prototype.Browser.IE && (Prototype.BrowserFeatures['Version'] === 6 || Prototype.BrowserFeatures['Version'] === 7)) {
return;
}
selects = $$(".chzn-select");
_results = [];
for (_i = 0, _len = selects.length; _i < _len; _i++) {
select = selects[_i];
_results.push(new Chosen(select));
}
deselects = $$(".chzn-select-deselect");
for (_i = 0, _len = deselects.length; _i < _len; _i++) {
select = deselects[_i];
_results.push(new Chosen(select,{allow_single_deselect:true}));
}
return _results;
});
</script>
</body>
orgia and The South Sandwich Islands">South Georgia and The South Sandwich Islands</option>
<option value="South Sudan">South Sudan</option> <option value="South Sudan">South Sudan</option>
<option value="Spain">Spain</option> <option value="Spain">Spain</option>
<option value="Sri Lanka">Sri Lanka</option> <option value="Sri Lanka">Sri Lanka</option>

18
admin/thirdparty/chosen/package.json vendored Normal file
View File

@ -0,0 +1,18 @@
{
"author": "harvest",
"name": "chosen",
"version": "0.9.7",
"description": "Chosen is a JavaScript plugin that makes long, unwieldy select boxes much more user-friendly. It is currently available in both jQuery and Prototype flavors.",
"repository": {
"type": "git",
"url": "https://github.com/harvesthq/chosen"
},
"engines": {
"node": ">=0.4.0"
},
"dependencies": {},
"devDependencies": {
"coffee-script": ">= 1.2",
"uglify-js": ">= 1.2.5"
}
}

View File

@ -4,18 +4,20 @@
.cms .ss-gridfield[data-selectable] td { cursor: pointer; } .cms .ss-gridfield[data-selectable] td { cursor: pointer; }
.cms table.ss-gridfield-table { display: table; box-shadow: none; padding: 0; border-collapse: separate; border-bottom: 0 none; width: 100%; } .cms table.ss-gridfield-table { display: table; box-shadow: none; padding: 0; border-collapse: separate; border-bottom: 0 none; width: 100%; }
.cms table.ss-gridfield-table thead { color: #1d2224; background: transparent; } .cms table.ss-gridfield-table thead { color: #1d2224; background: transparent; }
.cms table.ss-gridfield-table thead tr.filter-header .fieldgroup { max-width: 512px; }
.cms table.ss-gridfield-table tbody { background: #FFF; } .cms table.ss-gridfield-table tbody { background: #FFF; }
.cms table.ss-gridfield-table tbody td { /* Emulate a link by default */ }
.cms table.ss-gridfield-table tbody td button { border: none; background: none; margin: 0 0 0 2px; padding: 0; width: auto; text-shadow: none; } .cms table.ss-gridfield-table tbody td button { border: none; background: none; margin: 0 0 0 2px; padding: 0; width: auto; text-shadow: none; }
.cms table.ss-gridfield-table tbody td button.ui-state-hover { background: none; border: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; } .cms table.ss-gridfield-table tbody td button.gridfield-button-delete.ui-state-hover { background: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; }
.cms table.ss-gridfield-table tbody td button span.ui-button-text { text-indent: -9999em; background: url(../images/icons/decline.png) no-repeat 0 2px; padding: 0; width: 20px; height: 20px; } .cms table.ss-gridfield-table tbody td button.gridfield-button-delete.ui-state-active { border: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; }
.cms table.ss-gridfield-table tbody td a.edit-link { display: inline-block; height: 20px; width: 20px; text-indent: -9999em; background: url(../images/icons/document--pencil.png) no-repeat 0 1px; } .cms table.ss-gridfield-table tbody td button.gridfield-button-unlink.ui-state-hover { background: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; }
.cms table.ss-gridfield-table tbody td button.gridfield-button-unlink.ui-state-active { border: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; }
.cms table.ss-gridfield-table tbody td a.edit-link { display: inline-block; width: 20px; height: 20px; text-indent: -9999em; background: url(../images/icons/document--pencil.png) no-repeat 0 1px; }
.cms table.ss-gridfield-table tfoot { color: #1d2224; } .cms table.ss-gridfield-table tfoot { color: #1d2224; }
.cms table.ss-gridfield-table tfoot tr td { background: #95a5ab; padding: .7em; border-bottom: 1px solid rgba(0, 0, 0, 0.1); } .cms table.ss-gridfield-table tfoot tr td { background: #95a5ab; padding: .7em; border-bottom: 1px solid rgba(0, 0, 0, 0.1); }
.cms table.ss-gridfield-table tr.title { -moz-border-radius-topleft: 7px; -webkit-border-top-left-radius: 7px; -o-border-top-left-radius: 7px; -ms-border-top-left-radius: 7px; -khtml-border-top-left-radius: 7px; border-top-left-radius: 7px; -moz-border-radius-topright: 7px; -webkit-border-top-right-radius: 7px; -o-border-top-right-radius: 7px; -ms-border-top-right-radius: 7px; -khtml-border-top-right-radius: 7px; border-top-right-radius: 7px; } .cms table.ss-gridfield-table tr.title { -moz-border-radius-topleft: 7px; -webkit-border-top-left-radius: 7px; -o-border-top-left-radius: 7px; -ms-border-top-left-radius: 7px; -khtml-border-top-left-radius: 7px; border-top-left-radius: 7px; -moz-border-radius-topright: 7px; -webkit-border-top-right-radius: 7px; -o-border-top-right-radius: 7px; -ms-border-top-right-radius: 7px; -khtml-border-top-right-radius: 7px; border-top-right-radius: 7px; }
.cms table.ss-gridfield-table tr.title th { position: relative; background: #7f9198; border-top: 1px solid rgba(0, 0, 0, 0.1); padding: 5px; min-height: 40px; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #b1c0c5), color-stop(100%, #7f9198)); background-image: -webkit-linear-gradient(#b1c0c5, #7f9198); background-image: -moz-linear-gradient(#b1c0c5, #7f9198); background-image: -o-linear-gradient(#b1c0c5, #7f9198); background-image: -ms-linear-gradient(#b1c0c5, #7f9198); background-image: linear-gradient(#b1c0c5, #7f9198); -moz-border-radius-topleft: 7px; -webkit-border-top-left-radius: 7px; -o-border-top-left-radius: 7px; -ms-border-top-left-radius: 7px; -khtml-border-top-left-radius: 7px; border-top-left-radius: 7px; -moz-border-radius-topright: 7px; -webkit-border-top-right-radius: 7px; -o-border-top-right-radius: 7px; -ms-border-top-right-radius: 7px; -khtml-border-top-right-radius: 7px; border-top-right-radius: 7px; text-shadow: rgba(0, 0, 0, 0.3) 0px -1px 0; } .cms table.ss-gridfield-table tr.title th { position: relative; background: #7f9198; border-top: 1px solid rgba(0, 0, 0, 0.1); padding: 5px; min-height: 40px; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #b1c0c5), color-stop(100%, #7f9198)); background-image: -webkit-linear-gradient(#b1c0c5, #7f9198); background-image: -moz-linear-gradient(#b1c0c5, #7f9198); background-image: -o-linear-gradient(#b1c0c5, #7f9198); background-image: -ms-linear-gradient(#b1c0c5, #7f9198); background-image: linear-gradient(#b1c0c5, #7f9198); -moz-border-radius-topleft: 7px; -webkit-border-top-left-radius: 7px; -o-border-top-left-radius: 7px; -ms-border-top-left-radius: 7px; -khtml-border-top-left-radius: 7px; border-top-left-radius: 7px; -moz-border-radius-topright: 7px; -webkit-border-top-right-radius: 7px; -o-border-top-right-radius: 7px; -ms-border-top-right-radius: 7px; -khtml-border-top-right-radius: 7px; border-top-right-radius: 7px; text-shadow: rgba(0, 0, 0, 0.3) 0px -1px 0; }
.cms table.ss-gridfield-table tr.title th h2 { padding: 0px; font-size: 16.8px; color: #fff; margin: 3px 8px 0; display: inline-block; } .cms table.ss-gridfield-table tr.title th h2 { padding: 0px; font-size: 16.8px; color: #fff; margin: 3px 8px 0; display: inline-block; }
.cms table.ss-gridfield-table tr.title th .new { font-size: 14.4px; border-color: rgba(0, 0, 0, 0.1); float: right; } .cms table.ss-gridfield-table tr.title th .new { font-size: 14.4px; float: right; }
.cms table.ss-gridfield-table tr.sortable-header { background: #bac8ce; } .cms table.ss-gridfield-table tr.sortable-header { background: #bac8ce; }
.cms table.ss-gridfield-table tr.sortable-header th { padding: 0px; } .cms table.ss-gridfield-table tr.sortable-header th { padding: 0px; }
.cms table.ss-gridfield-table tr:hover { background: #FFFAD6 !important; } .cms table.ss-gridfield-table tr:hover { background: #FFFAD6 !important; }
@ -44,15 +46,27 @@
.cms table.ss-gridfield-table tr th button.ss-gridfield-sort.ss-gridfield-sorted-desc { background-position: right -72px; } .cms table.ss-gridfield-table tr th button.ss-gridfield-sort.ss-gridfield-sorted-desc { background-position: right -72px; }
.cms table.ss-gridfield-table tr th button.ss-gridfield-sort.ss-gridfield-sorted-asc { background-position: right -116px; } .cms table.ss-gridfield-table tr th button.ss-gridfield-sort.ss-gridfield-sorted-asc { background-position: right -116px; }
.cms table.ss-gridfield-table tr th button.ss-gridfield-button-filter.ss-ui-button { position: absolute; right: 7px; top: -28px; display: block; text-indent: -9999em; width: 30px; height: 28px; border-top-left-radius: 0px; border-bottom-left-radius: 0px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-width: 1px; border-color: #9a9a9a; background: url(../images/icons/filter-icons.png) no-repeat -40px 6px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background: url(../images/icons/filter-icons.png) no-repeat -40px 6px, -webkit-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat -40px 6px, -moz-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat -40px 6px, -o-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat -40px 6px, -ms-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat -40px 6px, linear-gradient(#ffffff, #d9d9d9); } .cms table.ss-gridfield-table tr th button.ss-gridfield-button-filter.ss-ui-button { position: absolute; right: 7px; top: -28px; display: block; text-indent: -9999em; width: 30px; height: 28px; border-top-left-radius: 0px; border-bottom-left-radius: 0px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-width: 1px; border-color: #9a9a9a; background: url(../images/icons/filter-icons.png) no-repeat -40px 6px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background: url(../images/icons/filter-icons.png) no-repeat -40px 6px, -webkit-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat -40px 6px, -moz-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat -40px 6px, -o-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat -40px 6px, -ms-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat -40px 6px, linear-gradient(#ffffff, #d9d9d9); }
.cms table.ss-gridfield-table tr th button.ss-gridfield-button-filter.ss-ui-button:hover { background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #4199cd), color-stop(100%, #2e7ead)); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -webkit-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -moz-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -o-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -ms-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, linear-gradient(#4199cd, #2e7ead); } .cms table.ss-gridfield-table tr th button.ss-gridfield-button-filter.ss-ui-button.hover-alike:active { background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #4199cd), color-stop(100%, #2e7ead)); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -webkit-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -moz-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -o-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -ms-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, linear-gradient(#4199cd, #2e7ead); -moz-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); -webkit-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); -o-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); }
.cms table.ss-gridfield-table tr th button.ss-gridfield-button-filter.ss-ui-button:active { background: url(../images/icons/filter-icons.png) no-repeat -16px -17px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #4199cd), color-stop(100%, #2e7ead)); background: url(../images/icons/filter-icons.png) no-repeat -16px -17px, -webkit-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px -17px, -moz-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px -17px, -o-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px -17px, -ms-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px -17px, linear-gradient(#4199cd, #2e7ead); -moz-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); -webkit-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); -o-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); } .cms table.ss-gridfield-table tr th button.ss-gridfield-button-filter.ss-ui-button.hover-alike { background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #4199cd), color-stop(100%, #2e7ead)); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -webkit-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -moz-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -o-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -ms-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, linear-gradient(#4199cd, #2e7ead); }
.cms table.ss-gridfield-table tr th button.ss-gridfield-button-reset.ss-ui-button { position: absolute; right: 36px; top: -28px; display: block; text-indent: -9999em; width: 30px; height: 28px; float: right; border-radius: 0px; border-bottom-width: 1px; border-color: #9a9a9a; background: url(../images/icons/filter-icons.png) no-repeat 8px 5px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background: url(../images/icons/filter-icons.png) no-repeat 8px 5px, -webkit-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat 8px 5px, -moz-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat 8px 5px, -o-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat 8px 5px, -ms-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat 8px 5px, linear-gradient(#ffffff, #d9d9d9); } .cms table.ss-gridfield-table tr th button.ss-gridfield-button-reset.ss-ui-button { position: absolute; right: 36px; top: -28px; display: block; text-indent: -9999em; width: 30px; height: 28px; float: right; border-radius: 0px; border-bottom-width: 1px; border-color: #9a9a9a; background: url(../images/icons/filter-icons.png) no-repeat 8px 5px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background: url(../images/icons/filter-icons.png) no-repeat 8px 5px, -webkit-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat 8px 5px, -moz-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat 8px 5px, -o-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat 8px 5px, -ms-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat 8px 5px, linear-gradient(#ffffff, #d9d9d9); }
.cms table.ss-gridfield-table tr th button.ss-gridfield-button-reset.ss-ui-button:hover { background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ff0000), color-stop(100%, #cc0000)); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -webkit-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -moz-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -o-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -ms-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, linear-gradient(#ff0000, #cc0000); } .cms table.ss-gridfield-table tr th button.ss-gridfield-button-reset.ss-ui-button.filtered:hover { background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ff0000), color-stop(100%, #cc0000)); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -webkit-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -moz-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -o-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -ms-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, linear-gradient(#ff0000, #cc0000); }
.cms table.ss-gridfield-table tr th button.ss-gridfield-button-reset.ss-ui-button:active { background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ff0000), color-stop(100%, #cc0000)); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -webkit-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -moz-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -o-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -ms-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, linear-gradient(#ff0000, #cc0000); -moz-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); -webkit-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); -o-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); } .cms table.ss-gridfield-table tr th button.ss-gridfield-button-reset.ss-ui-button.filtered:active { background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ff0000), color-stop(100%, #cc0000)); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -webkit-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -moz-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -o-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -ms-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, linear-gradient(#ff0000, #cc0000); -moz-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); -webkit-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); -o-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); }
.cms table.ss-gridfield-table tr th input.ss-gridfield-sort { padding: 2px; } .cms table.ss-gridfield-table tr th input.ss-gridfield-sort { padding: 2px; }
.cms table.ss-gridfield-table tr th input.ss-gridfield-sort:focus { -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; } .cms table.ss-gridfield-table tr th input.ss-gridfield-sort:focus { -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; }
.cms table.ss-gridfield-table tr th span.non-sortable { padding: .4em 1em; } .cms table.ss-gridfield-table tr th span.non-sortable { padding: 1em 1em; display: block; }
.cms table.ss-gridfield-table tr td { border-right: 1px solid rgba(0, 0, 0, 0.1); padding: 6.4px 12px; color: #666666; } .cms table.ss-gridfield-table tr td { border-right: 1px solid rgba(0, 0, 0, 0.1); padding: 6.4px 12px; color: #666666; }
.cms table.ss-gridfield-table tr td.bottom-all { -moz-border-radius-bottomleft: 7px; -webkit-border-bottom-left-radius: 7px; -o-border-bottom-left-radius: 7px; -ms-border-bottom-left-radius: 7px; -khtml-border-bottom-left-radius: 7px; border-bottom-left-radius: 7px; -moz-border-radius-bottomright: 7px; -webkit-border-bottom-right-radius: 7px; -o-border-bottom-right-radius: 7px; -ms-border-bottom-right-radius: 7px; -khtml-border-bottom-right-radius: 7px; border-bottom-right-radius: 7px; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #b1c0c5), color-stop(100%, #7f9198)); background-image: -webkit-linear-gradient(#b1c0c5, #7f9198); background-image: -moz-linear-gradient(#b1c0c5, #7f9198); background-image: -o-linear-gradient(#b1c0c5, #7f9198); background-image: -ms-linear-gradient(#b1c0c5, #7f9198); background-image: linear-gradient(#b1c0c5, #7f9198); } .cms table.ss-gridfield-table tr td.bottom-all { -moz-border-radius-bottomleft: 7px; -webkit-border-bottom-left-radius: 7px; -o-border-bottom-left-radius: 7px; -ms-border-bottom-left-radius: 7px; -khtml-border-bottom-left-radius: 7px; border-bottom-left-radius: 7px; -moz-border-radius-bottomright: 7px; -webkit-border-bottom-right-radius: 7px; -o-border-bottom-right-radius: 7px; -ms-border-bottom-right-radius: 7px; -khtml-border-bottom-right-radius: 7px; border-bottom-right-radius: 7px; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #b1c0c5), color-stop(100%, #7f9198)); background-image: -webkit-linear-gradient(#b1c0c5, #7f9198); background-image: -moz-linear-gradient(#b1c0c5, #7f9198); background-image: -o-linear-gradient(#b1c0c5, #7f9198); background-image: -ms-linear-gradient(#b1c0c5, #7f9198); background-image: linear-gradient(#b1c0c5, #7f9198); }
.cms table.ss-gridfield-table tr td.bottom-all .datagrid-pagination { padding-top: 2px; position: absolute; left: 50%; margin-left: -116px; }
.cms table.ss-gridfield-table tr td.bottom-all .datagrid-pagination .pagination-page-number { color: white; }
.cms table.ss-gridfield-table tr td.bottom-all .datagrid-pagination .pagination-page-number input { width: 35px; height: 20px; margin-bottom: -6px; padding: 0px; }
.cms table.ss-gridfield-table tr td.bottom-all .datagrid-pagination button.ss-gridfield-previouspage { background: url(../images/icons/pagination-arrows.png) no-repeat -23px 7px; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; border: none; width: 10px; margin: 0 10px; }
.cms table.ss-gridfield-table tr td.bottom-all .datagrid-pagination button.ss-gridfield-previouspage span { text-indent: -9999em; }
.cms table.ss-gridfield-table tr td.bottom-all .datagrid-pagination button.ss-gridfield-nextpage { background: url(../images/icons/pagination-arrows.png) no-repeat -47px 7px; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; border: none; width: 10px; margin: 0 10px; }
.cms table.ss-gridfield-table tr td.bottom-all .datagrid-pagination button.ss-gridfield-nextpage span { text-indent: -9999em; }
.cms table.ss-gridfield-table tr td.bottom-all .datagrid-pagination button.ss-gridfield-firstpage { background: url(../images/icons/pagination-arrows.png) no-repeat 0px 7px; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; border: none; width: 10px; margin: 0 10px; }
.cms table.ss-gridfield-table tr td.bottom-all .datagrid-pagination button.ss-gridfield-firstpage span { text-indent: -9999em; }
.cms table.ss-gridfield-table tr td.bottom-all .datagrid-pagination button.ss-gridfield-lastpage { background: url(../images/icons/pagination-arrows.png) no-repeat -73px 7px; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; border: none; width: 10px; margin: 0 10px; }
.cms table.ss-gridfield-table tr td.bottom-all .datagrid-pagination button.ss-gridfield-lastpage span { text-indent: -9999em; }
.cms table.ss-gridfield-table tr td.bottom-all .pagination-records-number { float: right; padding: 6px 0; color: white; }
.cms table.ss-gridfield-table tr.last td { border-bottom: 0 none; } .cms table.ss-gridfield-table tr.last td { border-bottom: 0 none; }
.cms table.ss-gridfield-table td:first-child, .cms table.ss-gridfield-table th:first-child { border-left: 1px solid rgba(0, 0, 0, 0.1); } .cms table.ss-gridfield-table td:first-child, .cms table.ss-gridfield-table th:first-child { border-left: 1px solid rgba(0, 0, 0, 0.1); }

View File

@ -192,3 +192,4 @@ BreadcrumbsTemplate.ss from cms/template to your theme or application.
* `DataObjectLog`: There is no replacement for this. * `DataObjectLog`: There is no replacement for this.
* `GeoIP`: Moved to separate ["geoip" module](https://github.com/silverstripe-labs/silverstripe-geoip) * `GeoIP`: Moved to separate ["geoip" module](https://github.com/silverstripe-labs/silverstripe-geoip)
* `NZGovtPasswordValidator`: Moved to ["securityextras" module](https://github.com/silverstripe-labs/silverstripe-securityextras) * `NZGovtPasswordValidator`: Moved to ["securityextras" module](https://github.com/silverstripe-labs/silverstripe-securityextras)
* `MemberTableField`: Use GridField with GridFieldConfig_RelationEditor instead

View File

@ -36,6 +36,7 @@ Each file describes its purpose at the top of the declarations. Note that you ca
plain CSS without SCSS for your custom CMS interfaces as well, we just mandate SCSS for core usage. plain CSS without SCSS for your custom CMS interfaces as well, we just mandate SCSS for core usage.
As there's a whole lot of CSS driving the CMS, we have certain best practives around writing it: As there's a whole lot of CSS driving the CMS, we have certain best practives around writing it:
* Use the `id` attribute sparingly. Remember that it "closes off" the structure to code reuse, as HTML elements * Use the `id` attribute sparingly. Remember that it "closes off" the structure to code reuse, as HTML elements
require unique `id` attributes. Code reuse can happen both in CSS and JavaScript behaviour. require unique `id` attributes. Code reuse can happen both in CSS and JavaScript behaviour.
* Separate presentation from structure in class names, e.g. `left-menu` is encoding the component position * Separate presentation from structure in class names, e.g. `left-menu` is encoding the component position
@ -178,6 +179,56 @@ Alternatively, form-related Ajax calls can be invoked through their own wrappers
which don't cause history events and hence allow callbacks: `$('.cms-content').loadForm()` which don't cause history events and hence allow callbacks: `$('.cms-content').loadForm()`
and `$('.cms-content').submitForm()`. and `$('.cms-content').submitForm()`.
## State through HTTP response metadata
By loading mostly HTML responses, we don't have an easy way to communicate
information which can't be directly contained in the produced HTML.
For example, the currently used controller class might've changed due to a "redirect",
which affects the currently active menu entry. We're using HTTP response headers to contain this data
without affecting the response body.
:::php
class MyController extends LeftAndMain {
class myaction() {
// ...
$this->response->addHeader('X-Controller', 'MyOtherController');
return $html;
}
}
Built-in headers are:
* `X-Controller`: PHP class name matching a menu entry, which is marked active
* `X-ControllerURL`: Alternative URL to record in the HTML5 browser history
* `X-Status`: Extended status information, used for an information popover.
## Special Links
Some links should do more than load a new page in the browser window.
To avoid repetition, we've written some helpers for various use cases:
* Load into a panel: `<a href="..." class="cms-panel-link" data-target-panel=".cms-content">`
* Load via ajax, and show response status message: `<a href="..." class="cms-link-ajax">`
* Load URL as an iframe into a popup/dialog: `<a href="..." class="ss-ui-dialog-link">`
## Buttons
SilverStripe automatically applies a [jQuery UI button style](http://jqueryui.com/demos/button/)
to all elements with the class `.ss-ui-button`. We've extended the jQuery UI widget a bit
to support defining icons via HTML5 data attributes (see `ssui.core.js`).
These icon identifiers relate to icon files in `sapphire/admin/images/btn-icons`,
and are sprited into a single file through SCSS and the Compass framework
(see [tutorial](http://compass-style.org/help/tutorials/spriting/)).
Compass also creates the correct CSS classes to show those sprites via background images
(see `sapphire/admin/scss/_sprites.scss`).
Input: `<a href="..." class="ss-ui-button" data-icon="add" />Button text</a>`
Output: `<a href="..." data-icon="add" class="ss-ui-button ss-ui-action-constructive ui-button ui-widget ui-state-default ui-corner-all ui-button-text-icon-primary" role="button"><span class="ui-button-icon-primary ui-icon btn-icon-add"></span><span class="ui-button-text">Button text</span></a>`
Note that you can create buttons from pretty much any element, although
when using an input of type button, submit or reset, support is limited to plain text labels with no icons.
## Menu ## Menu
The navigation menu in the CMS is created through the `[api:CMSMenu]` API, The navigation menu in the CMS is created through the `[api:CMSMenu]` API,

View File

@ -2,7 +2,7 @@
The `GridField` is a flexible form field for creating tables of data. It's new in SilverStripe 3.0 and replaces `ComplexTableField`, `TableListField`, and `TableField`. It's built as a lean core with a number of components that you plug into it. By selecting from the components that we provide or writing your own, you can grid a wide variety of grid controls. The `GridField` is a flexible form field for creating tables of data. It's new in SilverStripe 3.0 and replaces `ComplexTableField`, `TableListField`, and `TableField`. It's built as a lean core with a number of components that you plug into it. By selecting from the components that we provide or writing your own, you can grid a wide variety of grid controls.
# Using GridField ## Using GridField
A GridField is created like any other field: you create an instance of the GridField object and add it to the fields of a form. At its simplest, GridField takes 3 arguments: field name, field title, and an `SS_List` of records to display. A GridField is created like any other field: you create an instance of the GridField object and add it to the fields of a form. At its simplest, GridField takes 3 arguments: field name, field title, and an `SS_List` of records to display.
@ -19,11 +19,11 @@ This example might come from a Controller designed to manage the members of a gr
Note that the only way to specify the data that is listed in a grid field is with `SS_List` argument. If you want to customise the data displayed, you can do so by customising this object. Note that the only way to specify the data that is listed in a grid field is with `SS_List` argument. If you want to customise the data displayed, you can do so by customising this object.
This will create a read-only grid field that will show the columns specified in the Member's `$summary_fields` setting, and will let you sort and/or filter by those columns, as well as show pagination controls with 25 records per page. This will create a read-only grid field that will show the columns specified in the Member's `$summary_fields` setting, and will let you sort and/or filter by those columns, as well as show pagination controls with a handful of records per page.
## GridFieldConfig ## GridFieldConfig: Portable configuration
This grid create above a useful default case, but when developing applications you may need to control the behaviour of your grid more precisely than this. To this end, the `GridField` constructor allows for 4th argument, `$config`, where you can pass a `GridFieldConfig` object. The example above a useful default case, but when developing applications you may need to control the behaviour of your grid more precisely than this. To this end, the `GridField` constructor allows for fourth argument, `$config`, where you can pass a `GridFieldConfig` object.
This example creates exactly the same kind of grid as the previous example, but it creates the configuration manually: This example creates exactly the same kind of grid as the previous example, but it creates the configuration manually:
@ -49,10 +49,12 @@ If we wanted to make a simpler grid without pagination or filtering, we could do
$config->addComponent(new GridFieldPaginator(25)); $config->addComponent(new GridFieldPaginator(25));
$field = new GridField("Members", "Members of this group", $this->group->Members(), $config); $field = new GridField("Members", "Members of this group", $this->group->Members(), $config);
A `GridFieldConfig` is made up of a new of `GridFieldComponent` objects. `GridFieldComponent` is a family of interfaces. A `GridFieldConfig` is made up of a new of `GridFieldComponent` objects, which are described in the next chapter.
## Built-in components
## GridFieldComponent: Modular features
`GridFieldComponent` is a family of interfaces.
SilverStripe Framework comes with the following components that you can use out of the box. SilverStripe Framework comes with the following components that you can use out of the box.
### GridFieldDefaultColumns ### GridFieldDefaultColumns
@ -91,7 +93,7 @@ You can also specify formatting replacements, to replace column contents with HT
'Email' => '<strong>$Email</strong>', 'Email' => '<strong>$Email</strong>',
)); ));
**EXPERIMENTAL API WARNING:** We will most likely refactor this so that this configuration methods are called on the component rather than the grid field. Whoever does this should also update this documentation page. :-) **EXPERIMENTAL API WARNING:** We will most likely refactor this so that this configuration methods are called on the component rather than the grid field.
### GridFieldSortableHeader ### GridFieldSortableHeader
@ -105,7 +107,40 @@ This component will add a header row with a text field filter for each column, l
This component will limit output to a fixed number of items per page add a footer row with pagination controls. The constructor takes 1 argument: the number of items per page. This component will limit output to a fixed number of items per page add a footer row with pagination controls. The constructor takes 1 argument: the number of items per page.
# Extending GridField with custom components ### GridFieldAction
TODO Describe component, including GridFieldEditAction/GridFieldDeleteAction
### GridFieldRelationAdd
This class is is responsible for adding objects to another object's has_many and many_many relation,
as defined by the `[api:RelationList]` passed to the GridField constructor.
Objects can be searched through an input field (partially matching one or more fields).
Selecting from the results will add the object to the relation.
Often used alongside `[api:GridFieldRelationDelete]` for detaching existing records from a relatinship.
For easier setup, have a look at a sample configuration in `[api:GridFieldConfig_RelationEditor]`.
### GridFieldRelationDelete
Allows to detach an item from an existing has_many or many_many relationship.
Similar to {@link GridFieldDeleteAction}, but allows to distinguish between
a "delete" and "detach" action in the UI - and to use both in parallel, if required.
Requires the GridField to be populated with a `[api:RelationList]` rather than a plain DataList.
Often used alongside `[api:GridFieldRelationAdd]` to add existing records to the relationship.
### GridFieldPopupForms
TODO Describe component, including how it relates to GridFieldEditAction. Point to GridFieldConfig_RelationEditor for easier defaults.
### GridFieldTitle
TODO
### GridFieldExporter
TODO
## Extending GridField with custom components
You can create a custom component by building a class that implements one or more of the following interfaces: `GridField_HTMLProvider`, `GridField_ColumnProvider`, `GridField_ActionProvider`, or `GridField_DataManipulator`. You can create a custom component by building a class that implements one or more of the following interfaces: `GridField_HTMLProvider`, `GridField_ColumnProvider`, `GridField_ActionProvider`, or `GridField_DataManipulator`.
@ -196,7 +231,7 @@ To provide your actions, define the following two functions:
* **`function getActions($gridField)`:** Return a list of actions that this component provides. There is no namespacing on these actions, so you need to ensure that they don't conflict with other components. * **`function getActions($gridField)`:** Return a list of actions that this component provides. There is no namespacing on these actions, so you need to ensure that they don't conflict with other components.
* **`function handleAction(GridField $gridField, $actionName, $arguments, $data)`:** Handle the action defined by `$actionName` and `$arguments`. `$data` will contain the full data from the form, if you need to access that. * **`function handleAction(GridField $gridField, $actionName, $arguments, $data)`:** Handle the action defined by `$actionName` and `$arguments`. `$data` will contain the full data from the form, if you need to access that.
To call your actions, you need to create `GridField_Action` elsewhere in your component. Read more about them below. To call your actions, you need to create `GridField_FormAction` elsewhere in your component. Read more about them below.
**EXPERIMENTAL API WARNING:** handleAction implementations often contain a big switch statement and this interface might be amended on, such that each action is defined in a separate method. If we do this, it will be done before 3.0 stable so that we can lock down the API, but early adopters should be aware of this potential for change! **EXPERIMENTAL API WARNING:** handleAction implementations often contain a big switch statement and this interface might be amended on, such that each action is defined in a separate method. If we do this, it will be done before 3.0 stable so that we can lock down the API, but early adopters should be aware of this potential for change!

View File

@ -363,7 +363,11 @@ class File extends DataObject {
)->setName("FilePreview")->addExtraClass('cms-file-info'); )->setName("FilePreview")->addExtraClass('cms-file-info');
$urlField->dontEscape = true; $urlField->dontEscape = true;
return new FieldList( //get a tree listing with only folder, no files
$folderTree = new TreeDropdownField("ParentID", _t('AssetTableField.FOLDER','Folder'), 'Folder');
$folderTree->setChildrenMethod('ChildFolders');
$fields = new FieldList(
new TabSet('Root', new TabSet('Root',
new Tab('Main', new Tab('Main',
$filePreview, $filePreview,
@ -371,12 +375,16 @@ class File extends DataObject {
// $uploadField, // $uploadField,
new TextField("Title", _t('AssetTableField.TITLE','Title')), new TextField("Title", _t('AssetTableField.TITLE','Title')),
new TextField("Name", _t('AssetTableField.FILENAME','Filename')), new TextField("Name", _t('AssetTableField.FILENAME','Filename')),
new DropdownField("OwnerID", _t('AssetTableField.OWNER','Owner'), Member::mapInCMSGroups()) new DropdownField("OwnerID", _t('AssetTableField.OWNER','Owner'), Member::mapInCMSGroups()),
$folderTree
) )
) )
); );
return $fields;
} }
/** /**
* Returns a category based on the file extension. * Returns a category based on the file extension.
* This can be useful when grouping files by type, * This can be useful when grouping files by type,

View File

@ -47,7 +47,7 @@ class AjaxUniqueTextField extends TextField {
'id' => $this->id(), 'id' => $this->id(),
'name' => $this->getName(), 'name' => $this->getName(),
'value' => $this->Value(), 'value' => $this->Value(),
'tabindex' => $this->getTabIndex(), 'tabindex' => $this->getAttribute('tabindex'),
'maxlength' => ($this->maxLength) ? $this->maxLength : null 'maxlength' => ($this->maxLength) ? $this->maxLength : null
); );

View File

@ -181,21 +181,16 @@ class CheckboxSetField extends OptionsetField {
* @param DataObject $record The record to save into * @param DataObject $record The record to save into
*/ */
function saveInto(DataObject $record) { function saveInto(DataObject $record) {
$fieldname = $this->name ; $fieldname = $this->name;
if($fieldname && $record && ($record->has_many($fieldname) || $record->many_many($fieldname))) { $relation = ($fieldname && $record && $record->hasMethod($fieldname)) ? $record->$fieldname() : null;
if($fieldname && $record && $relation && $relation instanceof RelationList) {
$idList = array(); $idList = array();
// Works for both <select multiple> style - array(0 => 'val1', 1 => 'val2') if($this->value) foreach($this->value as $id => $bool) {
// and <input type="checkbox"> style - array('val1' => true, 'val2' => true). if($bool) {
// The <select multiple> element doesn't allow for individual keys in parameter names. $idList[] = $id;
$valuesInKeys = (ArrayLib::is_associative($this->value)); }
if($this->value) foreach($this->value as $k => $v) {
if($valuesInKeys) {
if($v) $idList[] = $k;
} else {
$idList[] = $v;
}
} }
$record->$fieldname()->setByIDList($idList); $relation->setByIDList($idList);
} elseif($fieldname && $record) { } elseif($fieldname && $record) {
if($this->value) { if($this->value) {
$this->value = str_replace(',', '{comma}', $this->value); $this->value = str_replace(',', '{comma}', $this->value);
@ -220,6 +215,7 @@ class CheckboxSetField extends OptionsetField {
$filtered[] = str_replace(",", "{comma}", $item); $filtered[] = str_replace(",", "{comma}", $item);
} }
} }
return implode(',', $filtered); return implode(',', $filtered);
} }

View File

@ -113,7 +113,7 @@ class ConfirmedPasswordField extends FormField {
} }
$content .= "<div class=\"showOnClick\">\n"; $content .= "<div class=\"showOnClick\">\n";
$content .= "<a href=\"#\"" . $this->getTabIndexHTML() . ">{$title}</a>\n"; $content .= "<a href=\"#\">{$title}</a>\n";
$content .= "<div class=\"showOnClickContainer\">"; $content .= "<div class=\"showOnClickContainer\">";
} }

View File

@ -19,6 +19,17 @@ class CreditCardField extends TextField {
return $field; return $field;
} }
/**
* Get tabindex HTML string
*
* @param int $increment Increase current tabindex by this value
* @return string
*/
protected function getTabIndexHTML($increment = 0) {
$tabIndex = (int)$this->getTabIndex() + (int)$increment;
return (is_numeric($tabIndex)) ? ' tabindex = "' . $tabIndex . '"' : '';
}
function dataValue() { function dataValue() {
if(is_array($this->value)) return implode("", $this->value); if(is_array($this->value)) return implode("", $this->value);
else return $this->value; else return $this->value;

View File

@ -50,13 +50,6 @@ class FormField extends RequestHandler {
*/ */
protected $leftTitle; protected $leftTitle;
/**
* Set the "tabindex" HTML attribute on the field.
*
* @var int
*/
protected $tabIndex;
/** /**
* Stores a reference to the FieldList that contains this object. * Stores a reference to the FieldList that contains this object.
* @var FieldList * @var FieldList
@ -225,30 +218,24 @@ class FormField extends RequestHandler {
* Set tabindex HTML attribute * Set tabindex HTML attribute
* (defaults to none). * (defaults to none).
* *
* @deprecated 3.0 Use setAttribute("tabindex") instead
* @param int $index * @param int $index
*/ */
public function setTabIndex($index) { public function setTabIndex($index) {
$this->tabIndex = $index; Deprecation::notice('3.0', 'Use setAttribute("tabindex") instead');
$this->setAttribute($index);
return $this; return $this;
} }
/** /**
* Get tabindex (if previously set) * Get tabindex (if previously set)
*
* @deprecated 3.0 Use getAttribute("tabindex") instead
* @return int * @return int
*/ */
public function getTabIndex() { public function getTabIndex() {
return $this->tabIndex; Deprecation::notice('3.0', 'Use getAttribute("tabindex") instead');
} return $this->getAttribute('tabindex');
/**
* Get tabindex HTML string
*
* @param int $increment Increase current tabindex by this value
* @return string
*/
protected function getTabIndexHTML($increment = 0) {
$tabIndex = (int)$this->getTabIndex() + (int)$increment;
return (is_numeric($tabIndex)) ? ' tabindex = "' . $tabIndex . '"' : '';
} }
/** /**
@ -301,6 +288,12 @@ class FormField extends RequestHandler {
/** /**
* Set an HTML attribute on the field element, mostly an <input> tag. * Set an HTML attribute on the field element, mostly an <input> tag.
* *
* Some attributes are best set through more specialized methods, to avoid interfereing with built-in behaviour:
* - 'class': {@link addExtraClass()}
* - 'title': {@link setDescription()}
* - 'value': {@link setValue}
* - 'name': {@link setName}
*
* CAUTION Doesn't work on most fields which are composed of more than one HTML form field: * CAUTION Doesn't work on most fields which are composed of more than one HTML form field:
* AjaxUniqueTextField, CheckboxSetField, ComplexTableField, CompositeField, ConfirmedPasswordField, CountryDropdownField, * AjaxUniqueTextField, CheckboxSetField, ComplexTableField, CompositeField, ConfirmedPasswordField, CountryDropdownField,
* CreditCardField, CurrencyField, DateField, DatetimeField, FieldGroup, GridField, HtmlEditorField, * CreditCardField, CurrencyField, DateField, DatetimeField, FieldGroup, GridField, HtmlEditorField,
@ -335,8 +328,8 @@ class FormField extends RequestHandler {
'value' => $this->Value(), 'value' => $this->Value(),
'class' => $this->extraClass(), 'class' => $this->extraClass(),
'id' => $this->ID(), 'id' => $this->ID(),
'tabindex' => $this->getTabIndex(),
'disabled' => $this->isDisabled(), 'disabled' => $this->isDisabled(),
'title' => $this->getDescription(),
); );
return array_merge($attrs, $this->attributes); return array_merge($attrs, $this->attributes);
} }
@ -689,14 +682,22 @@ class FormField extends RequestHandler {
return true; return true;
} }
/**
* @deprecated 3.0 Use setDescription()
*/
function describe($description) {
Deprecation::notice('3.0', 'Use setDescription()');
$this->setDescription($description);
return $this;
}
/** /**
* Describe this field, provide help text for it. * Describe this field, provide help text for it.
* The function returns this so it can be used like this: * By default, renders as a "title" attribute on the form field.
* $action = FormAction::create('submit', 'Submit')->describe("Send your changes to be approved")
* *
* @return string Description * @return string Description
*/ */
function describe($description) { function setDescription($description) {
$this->description = $description; $this->description = $description;
return $this; return $this;
} }

View File

@ -27,6 +27,8 @@
* *
* @see http://doc.silverstripe.org/tutorial/5-dataobject-relationship-management * @see http://doc.silverstripe.org/tutorial/5-dataobject-relationship-management
* *
* @deprecated 3.0 Use GridField with GridFieldConfig_RelationEditor
*
* @package forms * @package forms
* @subpackage fields-relational * @subpackage fields-relational
*/ */
@ -49,6 +51,8 @@ class HasManyComplexTableField extends ComplexTableField {
function __construct($controller, $name, $sourceClass, $fieldList = null, $detailFormFields = null, $sourceFilter = "", $sourceSort = "", $sourceJoin = "") { function __construct($controller, $name, $sourceClass, $fieldList = null, $detailFormFields = null, $sourceFilter = "", $sourceSort = "", $sourceJoin = "") {
parent::__construct($controller, $name, $sourceClass, $fieldList, $detailFormFields, $sourceFilter, $sourceSort, $sourceJoin); parent::__construct($controller, $name, $sourceClass, $fieldList, $detailFormFields, $sourceFilter, $sourceSort, $sourceJoin);
Deprecation::notice('3.0', 'Use GridField with GridFieldConfig_RelationEditor');
$this->Markable = true; $this->Markable = true;
if($controllerClass = $this->controllerClass()) { if($controllerClass = $this->controllerClass()) {

View File

@ -13,7 +13,7 @@ class HtmlEditorField extends TextareaField {
*/ */
static $use_gzip = true; static $use_gzip = true;
protected $rows = 30; protected $rows = null;
/** /**
* Includes the JavaScript neccesary for this field to work using the {@link Requirements} system. * Includes the JavaScript neccesary for this field to work using the {@link Requirements} system.
@ -90,7 +90,6 @@ class HtmlEditorField extends TextareaField {
parent::getAttributes(), parent::getAttributes(),
array( array(
'tinymce' => 'true', 'tinymce' => 'true',
'style' => 'width: 97%; height: ' . ($this->rows * 16) . 'px', // prevents horizontal scrollbars
'value' => null, 'value' => null,
) )
); );

View File

@ -41,6 +41,16 @@ class ListboxField extends DropdownField {
*/ */
protected $multiple = false; protected $multiple = false;
/**
* @var Array
*/
protected $disabledItems = array();
/**
* @var Array
*/
protected $defaultItems = array();
/** /**
* Creates a new dropdown field. * Creates a new dropdown field.
* *
@ -61,43 +71,31 @@ class ListboxField extends DropdownField {
* Returns a <select> tag containing all the appropriate <option> tags * Returns a <select> tag containing all the appropriate <option> tags
*/ */
function Field($properties = array()) { function Field($properties = array()) {
if($this->multiple) { if($this->multiple) $this->name .= '[]';
$this->name .= '[]';
}
$options = array(); $options = array();
// We have an array of values // We have an array of values
if(is_array($this->value)){ if(is_array($this->value)){
// Loop through and figure out which values were selected. // Loop through and figure out which values were selected.
foreach($this->getSource() as $value => $title) { foreach($this->getSource() as $value => $title) {
// Loop through the array of values to find out if this value is selected.
$selected = "";
foreach($this->value as $v){
if($value == $v) {
$selected = " selected=\"selected\"";
break;
}
}
$options[] = new ArrayData(array( $options[] = new ArrayData(array(
'Title' => $title, 'Title' => $title,
'Value' => $value, 'Value' => $value,
'Selected' => $selected, 'Selected' => (in_array($value, $this->value) || in_array($value, $this->defaultItems)),
'Disabled' => $this->disabled || in_array($value, $this->disabledItems),
)); ));
} }
} else { } else {
// Listbox was based a singlular value, so treat it like a dropdown. // Listbox was based a singlular value, so treat it like a dropdown.
foreach($this->getSource() as $value => $title) { foreach($this->getSource() as $value => $title) {
$selected = $value == $this->value ? " selected=\"selected\"" : "";
$options[] = new ArrayData(array( $options[] = new ArrayData(array(
'Title' => $title, 'Title' => $title,
'Value' => $value, 'Value' => $value,
'Selected' => $selected, 'Selected' => ($value == $this->value || in_array($value, $this->defaultItems)),
'Disabled' => $this->disabled || in_array($value, $this->disabledItems),
)); ));
} }
} }
$properties = array_merge($properties, array('Options' => new ArrayList($options))); $properties = array_merge($properties, array('Options' => new ArrayList($options)));
return $this->customise($properties)->renderWith($this->getTemplate()); return $this->customise($properties)->renderWith($this->getTemplate());
} }
@ -144,17 +142,65 @@ class ListboxField extends DropdownField {
} }
/** /**
* @return String * Return the CheckboxSetField value as a string
* selected item keys.
*
* @return string
*/ */
function dataValue() { function dataValue() {
if($this->value && $this->multiple && is_array($this->value)) { if($this->value && is_array($this->value) && $this->multiple) {
return implode(',', $this->value); $filtered = array();
foreach($this->value as $item) {
if($item) {
$filtered[] = str_replace(",", "{comma}", $item);
}
}
return implode(',', $filtered);
} else { } else {
return parent::dataValue(); return parent::dataValue();
} }
} }
function setValue($val) { /**
* Save the current value of this field into a DataObject.
* If the field it is saving to is a has_many or many_many relationship,
* it is saved by setByIDList(), otherwise it creates a comma separated
* list for a standard DB text/varchar field.
*
* @param DataObject $record The record to save into
*/
function saveInto(DataObject $record) {
if($this->multiple) {
$fieldname = $this->name;
$relation = ($fieldname && $record && $record->hasMethod($fieldname)) ? $record->$fieldname() : null;
if($fieldname && $record && $relation && $relation instanceof RelationList) {
$idList = (is_array($this->value)) ? array_values($this->value) : array();
if(!$record->ID) $record->write(); // record needs to have an ID in order to set relationships
$relation->setByIDList($idList);
} elseif($fieldname && $record) {
if($this->value) {
$this->value = str_replace(',', '{comma}', $this->value);
$record->$fieldname = implode(",", $this->value);
} else {
$record->$fieldname = null;
}
}
} else {
parent::saveInto($record);
}
}
/**
* Load a value into this CheckboxSetField
*/
function setValue($val, $obj = null) {
// If we're not passed a value directly,
// we can look for it in a relation method on the object passed as a second arg
if(!$val && $obj && $obj instanceof DataObject && $obj->hasMethod($this->name)) {
$funcName = $this->name;
$val = array_values($obj->$funcName()->getIDList());
}
if($val) { if($val) {
if(!$this->multiple && is_array($val)) { if(!$this->multiple && is_array($val)) {
throw new InvalidArgumentException('No array values allowed with multiple=false'); throw new InvalidArgumentException('No array values allowed with multiple=false');
@ -168,7 +214,6 @@ class ListboxField extends DropdownField {
// Doesn't check against unknown values in order to allow for less rigid data handling. // Doesn't check against unknown values in order to allow for less rigid data handling.
// They're silently ignored and overwritten the next time the field is saved. // They're silently ignored and overwritten the next time the field is saved.
parent::setValue($parts); parent::setValue($parts);
} else { } else {
if(!in_array($val, array_keys($this->source))) { if(!in_array($val, array_keys($this->source))) {
@ -187,4 +232,41 @@ class ListboxField extends DropdownField {
return $this; return $this;
} }
/**
* Mark certain elements as disabled,
* regardless of the {@link setDisabled()} settings.
*
* @param array $items Collection of array keys, as defined in the $source array
*/
function setDisabledItems($items) {
$this->disabledItems = $items;
return $this;
}
/**
* @return Array
*/
function getDisabledItems() {
return $this->disabledItems;
}
/**
* Default selections, regardless of the {@link setValue()} settings.
* Note: Items marked as disabled through {@link setDisabledItems()} can still be
* selected by default through this method.
*
* @param Array $items Collection of array keys, as defined in the $source array
*/
function setDefaultItems($items) {
$this->defaultItems = $items;
return $this;
}
/**
* @return Array
*/
function getDefaultItems() {
return $this->defaultItems;
}
} }

View File

@ -30,6 +30,8 @@
* ); * );
* </code> * </code>
* *
* @deprecated 3.0 Use GridField with GridFieldConfig_RelationEditor
*
* @package forms * @package forms
* @subpackage fields-relational * @subpackage fields-relational
*/ */
@ -41,6 +43,8 @@ class ManyManyComplexTableField extends HasManyComplexTableField {
function __construct($controller, $name, $sourceClass, $fieldList = null, $detailFormFields = null, $sourceFilter = "", $sourceSort = "", $sourceJoin = "") { function __construct($controller, $name, $sourceClass, $fieldList = null, $detailFormFields = null, $sourceFilter = "", $sourceSort = "", $sourceJoin = "") {
Deprecation::notice('3.0', 'Use GridField with GridFieldConfig_RelationEditor');
parent::__construct($controller, $name, $sourceClass, $fieldList, $detailFormFields, $sourceFilter, $sourceSort, $sourceJoin); parent::__construct($controller, $name, $sourceClass, $fieldList, $detailFormFields, $sourceFilter, $sourceSort, $sourceJoin);
$classes = array_reverse(ClassInfo::ancestry($this->controllerClass())); $classes = array_reverse(ClassInfo::ancestry($this->controllerClass()));

View File

@ -6,9 +6,6 @@
class MemberDatetimeOptionsetField extends OptionsetField { class MemberDatetimeOptionsetField extends OptionsetField {
function Field() { function Field() {
Requirements::javascript(THIRDPARTY_DIR . '/thirdparty/jquery/jquery.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/MemberDatetimeOptionsetField.js');
$options = ''; $options = '';
$odd = 0; $odd = 0;
$source = $this->getSource(); $source = $this->getSource();
@ -45,8 +42,12 @@ class MemberDatetimeOptionsetField extends OptionsetField {
_t('MemberDatetimeOptionsetField.Preview', 'Preview'), _t('MemberDatetimeOptionsetField.Preview', 'Preview'),
Zend_Date::now()->toString($value) Zend_Date::now()->toString($value)
) : ''; ) : '';
$options .= "<a class=\"formattingHelpToggle\" href=\"#\">" . _t('MemberDatetimeOptionsetField.TOGGLEHELP', 'Toggle formatting help') . "</a>"; $options .= sprintf(
$options .= "<div class=\"formattingHelpText\">"; '<a class="cms-help-toggle" href="#%s">%s</a>',
$this->id() . '_Help',
_t('MemberDatetimeOptionsetField.TOGGLEHELP', 'Toggle formatting help')
);
$options .= "<div id=\"" . $this->id() . "_Help\">";
$options .= $this->getFormattingHelpText(); $options .= $this->getFormattingHelpText();
$options .= "</div>"; $options .= "</div>";
$options .= "</li>\n"; $options .= "</li>\n";

View File

@ -102,7 +102,7 @@ class SimpleImageField extends FileField {
"type" => "file", "type" => "file",
"name" => $this->name, "name" => $this->name,
"id" => $this->id(), "id" => $this->id(),
"tabindex" => $this->getTabIndex(), "tabindex" => $this->getAttribute('tabindex'),
'disabled' => $this->disabled 'disabled' => $this->disabled
) )
); );
@ -111,7 +111,7 @@ class SimpleImageField extends FileField {
"type" => "hidden", "type" => "hidden",
"name" => "MAX_FILE_SIZE", "name" => "MAX_FILE_SIZE",
"value" => $this->getValidator()->getAllowedMaxFileSize(), "value" => $this->getValidator()->getAllowedMaxFileSize(),
"tabindex" => $this->getTabIndex() "tabindex" => $this->getAttribute('tabindex'),
) )
); );
$html .= "</div>"; $html .= "</div>";

View File

@ -54,6 +54,10 @@ class TreeDropdownField extends FormField {
* @ignore * @ignore
*/ */
protected $sourceObject, $keyField, $labelField, $filterCallback, $searchCallback, $baseID = 0; protected $sourceObject, $keyField, $labelField, $filterCallback, $searchCallback, $baseID = 0;
/**
* @var string default child method in Hierarcy->getChildrenAsUL
*/
protected $childrenMethod = 'AllChildrenIncludingDeleted';
/** /**
* Used by field search to leave only the relevant entries * Used by field search to leave only the relevant entries
@ -143,6 +147,16 @@ class TreeDropdownField extends FormField {
return $this; return $this;
} }
/**
* @param $method The parameter to ChildrenMethod to use when calling Hierarchy->getChildrenAsUL in {@link Hierarchy}.
* The method specified determined the structure of the returned list. Use "ChildFolders" in place of the default
* to get a drop-down listing with only folders, i.e. not including the child elements in the currently selected folder.
* See {@link Hierarchy} for a complete list of possible methods.
*/
public function setChildrenMethod($method) {
$this->childrenMethod = $method;
}
/** /**
* @return string * @return string
*/ */
@ -246,9 +260,9 @@ class TreeDropdownField extends FormField {
' . $child->markingClasses() . "\"><a rel=\"$child->ID\">" . $child->' . $this->labelField . ' . "</a>"'; ' . $child->markingClasses() . "\"><a rel=\"$child->ID\">" . $child->' . $this->labelField . ' . "</a>"';
if($isSubTree) { if($isSubTree) {
return substr(trim($obj->getChildrenAsUL('', $eval, null, true)), 4, -5); return substr(trim($obj->getChildrenAsUL('', $eval, null, true, $this->childrenMethod)), 4, -5);
} else { } else {
return $obj->getChildrenAsUL('class="tree"', $eval, null, true); return $obj->getChildrenAsUL('class="tree"', $eval, null, true, $this->childrenMethod);
} }
} }

View File

@ -290,6 +290,17 @@ class GridField extends FormField {
* @return string * @return string
*/ */
public function FieldHolder() { public function FieldHolder() {
Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css');
Requirements::css(SAPPHIRE_DIR . '/css/GridField.css');
Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/jquery-ui.js');
Requirements::javascript(THIRDPARTY_DIR . '/json-js/json2.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/i18n.js');
Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang');
Requirements::javascript(THIRDPARTY_DIR . '/jquery-entwine/dist/jquery.entwine-dist.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/GridField.js');
// Get columns // Get columns
$columns = $this->getColumns(); $columns = $this->getColumns();
@ -319,32 +330,39 @@ class GridField extends FormField {
} }
} }
$total = $list->count(); $total = $list->count();
if($total > 0) {
foreach($list as $idx => $record) { foreach($list as $idx => $record) {
$rowContent = ''; $rowContent = '';
foreach($columns as $column) { foreach($columns as $column) {
$colContent = $this->getColumnContent($record, $column); $colContent = $this->getColumnContent($record, $column);
// A return value of null means this columns should be skipped altogether. // A return value of null means this columns should be skipped altogether.
if($colContent === null) continue; if($colContent === null) continue;
$colAttributes = $this->getColumnAttributes($record, $column); $colAttributes = $this->getColumnAttributes($record, $column);
$rowContent .= $this->createTag('td', $colAttributes, $colContent); $rowContent .= $this->createTag('td', $colAttributes, $colContent);
}
$classes = array('ss-gridfield-item');
if ($idx == 0) $classes[] = 'first';
if ($idx == $total-1) $classes[] = 'last';
$classes[] = ($idx % 2) ? 'even' : 'odd';
$row = $this->createTag(
'tr',
array(
"class" => implode(' ', $classes),
'data-id' => $record->ID,
// TODO Allow per-row customization similar to GridFieldDefaultColumns
'data-class' => $record->ClassName,
),
$rowContent
);
$content['body'][] = $row;
} }
} else { //display a message when the grid field is empty
$classes = array('ss-gridfield-item');
if ($idx == 0) $classes[] = 'first';
if ($idx == $total-1) $classes[] = 'last';
$classes[] = ($idx % 2) ? 'even' : 'odd';
$row = $this->createTag( $row = $this->createTag(
'tr', 'tr',
array( array("class" => 'ss-gridfield-item ss-gridfield-no-items'),
"class" => implode(' ', $classes), $this->createTag('td', array('colspan' => count($columns)), _t('GridField.NoItemsFound', 'No items found'))
'data-id' => $record->ID,
// TODO Allow per-row customization similar to GridFieldDefaultColumns
'data-class' => $record->ClassName,
),
$rowContent
); );
$content['body'][] = $row; $content['body'][] = $row;
} }
@ -651,13 +669,14 @@ class GridField extends FormField {
/** /**
* This class is the base class when you want to have an action that alters the state of the gridfield * This class is the base class when you want to have an action that alters the state of the gridfield,
* rendered as a button element.
* *
* @package sapphire * @package sapphire
* @subpackage forms * @subpackage forms
* *
*/ */
class GridField_Action extends FormAction { class GridField_FormAction extends FormAction {
/** /**
* *
@ -665,12 +684,6 @@ class GridField_Action extends FormAction {
*/ */
protected $gridField; protected $gridField;
/**
*
* @var string
*/
protected $buttonLabel;
/** /**
* *
* @var array * @var array
@ -684,8 +697,11 @@ class GridField_Action extends FormAction {
//protected $stateFields = array(); //protected $stateFields = array();
protected $actionName; protected $actionName;
protected $args = array(); protected $args = array();
public $useButtonTag = true;
/** /**
* *
* @param GridField $gridField * @param GridField $gridField
@ -694,12 +710,11 @@ class GridField_Action extends FormAction {
* @param type $actionName * @param type $actionName
* @param type $args * @param type $args
*/ */
public function __construct(GridField $gridField, $name, $label, $actionName, $args) { public function __construct(GridField $gridField, $name, $title, $actionName, $args) {
$this->gridField = $gridField; $this->gridField = $gridField;
$this->buttonLabel = $label;
$this->actionName = $actionName; $this->actionName = $actionName;
$this->args = $args; $this->args = $args;
parent::__construct($name); parent::__construct($name, $title);
} }
/** /**
@ -720,45 +735,26 @@ class GridField_Action extends FormAction {
return '%'.dechex(ord($match[0])); return '%'.dechex(ord($match[0]));
} }
/** public function getAttributes() {
* Default method used by Templates to render the form
*
* @return string HTML tag
*/
public function Field() {
Requirements::css(SAPPHIRE_DIR . '/css/GridField.css');
Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js');
Requirements::javascript(THIRDPARTY_DIR . '/json-js/json2.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/i18n.js');
Requirements::javascript(THIRDPARTY_DIR . '/jquery-entwine/dist/jquery.entwine-dist.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/GridField.js');
// Store state in session, and pass ID to client side // Store state in session, and pass ID to client side
$state = array( $state = array(
'grid' => $this->getNameFromParent(), 'grid' => $this->getNameFromParent(),
'actionName' => $this->actionName, 'actionName' => $this->actionName,
'args' => $this->args, 'args' => $this->args,
); );
$id = preg_replace('/[^\w]+/', '_', uniqid('', true)); $id = preg_replace('/[^\w]+/', '_', uniqid('', true));
Session::set($id, $state); Session::set($id, $state);
$actionData['StateID'] = $id; $actionData['StateID'] = $id;
// And generate field return array_merge(
$data = new ArrayData(array( parent::getAttributes(),
'Class' => ($this->extraClass() ? $this->extraClass() : '') . ($this->isReadonly() ? ' disabled' : ''), array(
'ID' => $this->id(), // Note: This field needs to be less than 65 chars, otherwise Suhosin security patch
// Note: This field needs to be less than 65 chars, otherwise Suhosin security patch // will strip it from the requests
// will strip it from the requests 'name' => 'action_gridFieldAlterAction'. '?' . http_build_query($actionData),
'Name' => 'action_gridFieldAlterAction'. '?' . http_build_query($actionData), 'data-url' => $this->gridField->Link(),
'Disabled' => $this->isReadonly(), )
'Label' => $this->buttonLabel, );
'DataURL' => $this->gridField->Link(),
));
return $data->renderWith('GridField_Action');
} }
/** /**

View File

@ -1,196 +0,0 @@
<?php
/**
* Provides the entry point to editing a single record presented by the grid.
* Doesn't show an edit view on its own or modifies the record, but rather relies on routing conventions
* established in {@link getColumnContent()}. The default routing applies to
* the {@link GridFieldPopupForms} component, which has to be added separately
* to the grid field configuration.
*/
class GridFieldAction_Edit implements GridField_ColumnProvider {
/**
* Add a column 'Delete'
*
* @param type $gridField
* @param array $columns
*/
public function augmentColumns($gridField, &$columns) {
if(!in_array('Actions', $columns))
$columns[] = 'Actions';
}
/**
* Return any special attributes that will be used for FormField::createTag()
*
* @param GridField $gridField
* @param DataObject $record
* @param string $columnName
* @return array
*/
public function getColumnAttributes($gridField, $record, $columnName) {
return array();
}
/**
* Add the title
*
* @param GridField $gridField
* @param string $columnName
* @return array
*/
public function getColumnMetadata($gridField, $columnName) {
if($columnName == 'Actions') {
return array('title' => '');
}
}
/**
* Which columns are handled by this component
*
* @param type $gridField
* @return type
*/
public function getColumnsHandled($gridField) {
return array('Actions');
}
/**
* Which GridField actions are this component handling
*
* @param GridField $gridField
* @return array
*/
public function getActions($gridField) {
return array();
}
/**
*
* @param GridField $gridField
* @param DataObject $record
* @param string $columnName
* @return string - the HTML for the column
*/
public function getColumnContent($gridField, $record, $columnName) {
$data = new ArrayData(array(
'Link' => Controller::join_links($gridField->Link('item'), $record->ID, 'edit')
));
return $data->renderWith('GridFieldAction_Edit');
}
/**
* Handle the actions and apply any changes to the GridField
*
* @param GridField $gridField
* @param string $actionName
* @param mixed $arguments
* @param array $data - form data
* @return void
*/
public function handleAction(GridField $gridField, $actionName, $arguments, $data) {
}
}
/**
* This class is an GridField Component that add Delete action for Objects in the GridField.
* See {@link GridFieldRelationDelete} for detaching an item from the current relationship instead.
*/
class GridFieldAction_Delete implements GridField_ColumnProvider, GridField_ActionProvider {
/**
* Add a column 'Delete'
*
* @param type $gridField
* @param array $columns
*/
public function augmentColumns($gridField, &$columns) {
if(!in_array('Actions', $columns))
$columns[] = 'Actions';
}
/**
* Return any special attributes that will be used for FormField::createTag()
*
* @param GridField $gridField
* @param DataObject $record
* @param string $columnName
* @return array
*/
public function getColumnAttributes($gridField, $record, $columnName) {
return array();
}
/**
* Add the title
*
* @param GridField $gridField
* @param string $columnName
* @return array
*/
public function getColumnMetadata($gridField, $columnName) {
if($columnName == 'Actions') {
return array('title' => '');
}
}
/**
* Which columns are handled by this component
*
* @param type $gridField
* @return type
*/
public function getColumnsHandled($gridField) {
return array('Actions');
}
/**
* Which GridField actions are this component handling
*
* @param GridField $gridField
* @return array
*/
public function getActions($gridField) {
return array('deleterecord');
}
/**
*
* @param GridField $gridField
* @param DataObject $record
* @param string $columnName
* @return string - the HTML for the column
*/
public function getColumnContent($gridField, $record, $columnName) {
$field = new GridField_Action(
$gridField,
'DeleteRecord'.$record->ID,
_t('GridAction.Delete', "delete"),
"deleterecord",
array('RecordID' => $record->ID)
);
$field->addExtraClass('gridfield-button-delete');
$output = $field->Field();
return $output;
}
/**
* Handle the actions and apply any changes to the GridField
*
* @param GridField $gridField
* @param string $actionName
* @param mixed $arguments
* @param array $data - form data
* @return void
*/
public function handleAction(GridField $gridField, $actionName, $arguments, $data) {
if($actionName == 'deleterecord') {
$id = $arguments['RecordID'];
// Always deletes a record. Use GridFieldRelationDelete to detach it from the current relationship.
$item = $gridField->getList()->byID($id);
if(!$item) return;
$item->delete();
}
}
}

View File

@ -4,52 +4,132 @@
* Base interface for all components that can be added to GridField. * Base interface for all components that can be added to GridField.
*/ */
interface GridFieldComponent { interface GridFieldComponent {
} }
/** /**
* A GridField manipulator that provides HTML for the header/footer rows, or for before/after the template * A GridField manipulator that provides HTML for the header/footer rows, or for before/after the template
*/ */
interface GridField_HTMLProvider extends GridFieldComponent { interface GridField_HTMLProvider extends GridFieldComponent {
/** /**
* Returns a map with 4 keys 'header', 'footer', 'before', 'after'. Each of these can contain an * Returns a map with 4 keys 'header', 'footer', 'before', 'after'. Each of these can contain an
* HTML fragment and each of these are optional. * HTML fragment and each of these are optional.
* @return Array
*/ */
function getHTMLFragments($gridField); function getHTMLFragments($gridField);
} }
/**
* Add a new column to the table display body, or modify existing columns.
* Used once per record/row.
*/
interface GridField_ColumnProvider extends GridFieldComponent { interface GridField_ColumnProvider extends GridFieldComponent {
/**
* Modify the list of columns displayed in the table.
* See {@link GridField->getDisplayFields()} and {@link GridFieldDefaultColumns}.
*
* @param GridField
* @param Array List reference of all column names.
*/
function augmentColumns($gridField, &$columns); function augmentColumns($gridField, &$columns);
/**
* Names of all columns which are affected by this component.
*
* @param GridField
* @return Array
*/
function getColumnsHandled($gridField); function getColumnsHandled($gridField);
/**
* HTML for the column, content of the <td> element.
*
* @param GridField
* @param DataObject Record displayed in this row
* @param String
* @return String HTML for the column. Return NULL to skip.
*/
function getColumnContent($gridField, $record, $columnName); function getColumnContent($gridField, $record, $columnName);
/**
* Attributes for the element containing the content returned by {@link getColumnContent()}.
*
* @param GridField
* @param DataObject Record displayed in this row
* @param String
* @return Array
*/
function getColumnAttributes($gridField, $record, $columnName); function getColumnAttributes($gridField, $record, $columnName);
/**
* Additional metadata about the column which can be used by other components,
* e.g. to set a title for a search column header.
*
* @param GridField
* @param String
* @return Array Map of arbitrary metadata identifiers to their values.
*/
function getColumnMetadata($gridField, $columnName); function getColumnMetadata($gridField, $columnName);
} }
/**
* An action is defined by two things: an action name, and zero or more named arguments.
* There is no built-in notion of a record-specific or column-specific action,
* but you may choose to define an argument such as ColumnName or RecordID in order to implement these.
* Does not provide interface elements to call those actions, see {@link GridField_FormAction}.
*/
interface GridField_ActionProvider extends GridFieldComponent { interface GridField_ActionProvider extends GridFieldComponent {
/** /**
* Return a list of the actions handled by this action provider * Return a list of the actions handled by this action provider.
* Used to identify the action later on through the $actionName parameter in {@link handleAction}.
* There is no namespacing on these actions, so you need to ensure that they don't conflict with other components.
*
* @param GridField
* @return Array with action identifier strings.
*/ */
function getActions($gridField); function getActions($gridField);
/** /**
* Handle an action on the given gridField. * Handle an action on the given grid field.
* Calls ALL components for every action handled, so the component
* needs to ensure it only accepts actions it is actually supposed to handle.
*
* @param GridField
* @param String Action identifier, see {@link getActions()}.
* @param Array Arguments relevant for this
* @param Array All form data
*/ */
function handleAction(GridField $gridField, $actionName, $arguments, $data); function handleAction(GridField $gridField, $actionName, $arguments, $data);
} }
/**
* Can modify the data list.
* For example, a paginating component can apply a limit, or a sorting component can apply a sort.
* Generally, the data manipulator will make use of to `GridState` variables to decide
* how to modify the data list (see {@link GridState}).
*/
interface GridField_DataManipulator extends GridFieldComponent { interface GridField_DataManipulator extends GridFieldComponent {
/** /**
* Manipulate the datalist as needed by this grid modifier. * Manipulate the datalist as needed by this grid modifier.
* Return the new DataList. *
* @param GridField
* @param SS_List
* @return DataList
*/ */
function getManipulatedData(GridField $gridField, SS_List $dataList); function getManipulatedData(GridField $gridField, SS_List $dataList);
} }
/**
* Sometimes an action isn't enough: you need to provide additional support URLs for the grid.
* These URLs may return user-visible content, for example a pop-up form for editing a record's details,
* or they may be support URLs for front-end functionality.
* For example a URL that will return JSON-formatted data for a javascript grid control.
*/
interface GridField_URLHandler extends GridFieldComponent { interface GridField_URLHandler extends GridFieldComponent {
/** /**
* Return URLs to be handled by this grid field, in an array the same form as $url_handlers. * Return URLs to be handled by this grid field, in an array the same form as $url_handlers.
*
* Handler methods will be called on the component, rather than the grid field. * Handler methods will be called on the component, rather than the grid field.
*
* The handlers will be passed two arguments, $gridField and $request
*/ */
function getURLHandlers($gridField); function getURLHandlers($gridField);
} }

View File

@ -1,7 +1,17 @@
<?php <?php
/** /**
* Description of GridFieldConfig * Encapsulates a collection of components following the {@link GridFieldComponent} interface.
* While the {@link GridField} itself has some configuration in the form of setters,
* most of the details are dealt with through components.
* *
* For example, you would add a {@link GridFieldPaginator} component to enable
* pagination on the listed records, and configure it through {@link GridFieldPaginator->setItemsPerPage()}.
*
* In order to reduce the amount of custom code required, the framework provides
* some default configurations for common use cases:
* - {@link GridFieldConfig_Base} (added by default to GridField)
* - {@link GridFieldConfig_RecordEditor}
* - {@link GridFieldConfig_RelationEditor}
*/ */
class GridFieldConfig { class GridFieldConfig {
@ -43,6 +53,27 @@ class GridFieldConfig {
return $this; return $this;
} }
/**
* @param GridFieldComponent $component
* @return GridFieldConfig $this
*/
public function removeComponent(GridFieldComponent $component) {
$this->getComponents()->remove($component);
return $this;
}
/**
* @param String Class name or interface
* @return GridFieldConfig $this
*/
public function removeComponentsByType($type) {
$components = $this->getComponentsByType($type);
foreach($components as $component) {
$this->removeComponent($component);
}
return $this;
}
/** /**
* @return ArrayList Of GridFieldComponent * @return ArrayList Of GridFieldComponent
*/ */
@ -80,6 +111,10 @@ class GridFieldConfig {
} }
} }
/**
* A simple readonly, paginated view of records,
* with sortable and searchable headers.
*/
class GridFieldConfig_Base extends GridFieldConfig { class GridFieldConfig_Base extends GridFieldConfig {
/** /**
@ -87,7 +122,7 @@ class GridFieldConfig_Base extends GridFieldConfig {
* @param int $itemsPerPage - How many items per page should show up per page * @param int $itemsPerPage - How many items per page should show up per page
* @return GridFieldConfig_Base * @return GridFieldConfig_Base
*/ */
public static function create($itemsPerPage=15){ public static function create($itemsPerPage=null){
return new GridFieldConfig_Base($itemsPerPage); return new GridFieldConfig_Base($itemsPerPage);
} }
@ -95,7 +130,7 @@ class GridFieldConfig_Base extends GridFieldConfig {
* *
* @param int $itemsPerPage - How many items per page should show up * @param int $itemsPerPage - How many items per page should show up
*/ */
public function __construct($itemsPerPage=15) { public function __construct($itemsPerPage=null) {
$this->addComponent(new GridFieldTitle()); $this->addComponent(new GridFieldTitle());
$this->addComponent(new GridFieldSortableHeader()); $this->addComponent(new GridFieldSortableHeader());
$this->addComponent(new GridFieldFilter()); $this->addComponent(new GridFieldFilter());
@ -105,40 +140,72 @@ class GridFieldConfig_Base extends GridFieldConfig {
} }
/** /**
* This GridFieldConfig bundles a common set of componentes used for displaying
* a gridfield with:
*
* - Relation adding
* - Sortable header
* - Default columns
* - Edit links on every item
* - Action for removing relationship
* - Paginator
* *
*/ */
class GridFieldConfig_ManyManyEditor extends GridFieldConfig { class GridFieldConfig_RecordEditor extends GridFieldConfig {
/** /**
* *
* @param string $fieldToSearch - Which field on the object should be searched for
* @param int $itemsPerPage - How many items per page should show up * @param int $itemsPerPage - How many items per page should show up
* @return GridFieldConfig_ManyManyEditor * @return GridFieldConfig_RecordEditor
*/ */
public static function create($fieldToSearch, $itemsPerPage=15){ public static function create($itemsPerPage=null){
return new GridFieldConfig_ManyManyEditor($fieldToSearch, $itemsPerPage=15); return new GridFieldConfig_RecordEditor($itemsPerPage);
} }
/** /**
* *
* @param string $fieldToSearch - Which field on the object should be searched for
* @param int $itemsPerPage - How many items per page should show up * @param int $itemsPerPage - How many items per page should show up
*/ */
public function __construct($fieldToSearch, $itemsPerPage=15) { public function __construct($itemsPerPage=null) {
$this->addComponent(new GridFieldRelationAdd($fieldToSearch)); $this->addComponent(new GridFieldTitle());
$this->addComponent(new GridFieldSortableHeader()); $this->addComponent(new GridFieldSortableHeader());
$this->addComponent(new GridFieldFilter()); $this->addComponent(new GridFieldFilter());
$this->addComponent(new GridFieldDefaultColumns()); $this->addComponent(new GridFieldDefaultColumns());
$this->addComponent(new GridFieldAction_Edit()); $this->addComponent(new GridFieldEditAction());
$this->addComponent(new GridFieldDeleteAction());
$this->addComponent(new GridFieldPaginator($itemsPerPage));
$this->addComponent(new GridFieldPopupForms());
}
}
/**
* Similar to {@link GridFieldConfig_RecordEditor}, but adds features
* to work on has-many or many-many relationships.
* Allows to search for existing records to add to the relationship,
* detach listed records from the relationship (rather than removing them from the database),
* and automatically add newly created records to it.
*
* To further configure the field, use {@link getComponentByType()},
* for example to change the field to search.
* <code>
* GridFieldConfig_RelationEditor::create()
* ->getComponentByType('GridFieldRelationAdd')->setSearchFields('MyField');
* </code>
*/
class GridFieldConfig_RelationEditor extends GridFieldConfig {
/**
*
* @param int $itemsPerPage - How many items per page should show up
* @return GridFieldConfig_RelationEditor
*/
public static function create($itemsPerPage=null){
return new GridFieldConfig_RelationEditor($itemsPerPage);
}
/**
*
* @param int $itemsPerPage - How many items per page should show up
*/
public function __construct($itemsPerPage=null) {
$this->addComponent(new GridFieldTitle());
$this->addComponent(new GridFieldRelationAdd());
$this->addComponent(new GridFieldSortableHeader());
$this->addComponent(new GridFieldFilter());
$this->addComponent(new GridFieldDefaultColumns());
$this->addComponent(new GridFieldEditAction());
$this->addComponent(new GridFieldRelationDelete()); $this->addComponent(new GridFieldRelationDelete());
$this->addComponent(new GridFieldPaginator($itemsPerPage)); $this->addComponent(new GridFieldPaginator($itemsPerPage));
$this->addComponent(new GridFieldPopupForms()); $this->addComponent(new GridFieldPopupForms());

View File

@ -0,0 +1,103 @@
<?php
/**
* This class is an GridField Component that add Delete action for Objects in the GridField.
* See {@link GridFieldRelationDelete} for detaching an item from the current relationship instead.
*/
class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_ActionProvider {
/**
* Add a column 'Delete'
*
* @param type $gridField
* @param array $columns
*/
public function augmentColumns($gridField, &$columns) {
if(!in_array('Actions', $columns))
$columns[] = 'Actions';
}
/**
* Return any special attributes that will be used for FormField::createTag()
*
* @param GridField $gridField
* @param DataObject $record
* @param string $columnName
* @return array
*/
public function getColumnAttributes($gridField, $record, $columnName) {
return array();
}
/**
* Add the title
*
* @param GridField $gridField
* @param string $columnName
* @return array
*/
public function getColumnMetadata($gridField, $columnName) {
if($columnName == 'Actions') {
return array('title' => '');
}
}
/**
* Which columns are handled by this component
*
* @param type $gridField
* @return type
*/
public function getColumnsHandled($gridField) {
return array('Actions');
}
/**
* Which GridField actions are this component handling
*
* @param GridField $gridField
* @return array
*/
public function getActions($gridField) {
return array('deleterecord');
}
/**
*
* @param GridField $gridField
* @param DataObject $record
* @param string $columnName
* @return string - the HTML for the column
*/
public function getColumnContent($gridField, $record, $columnName) {
$field = Object::create('GridField_FormAction',
$gridField,
'DeleteRecord'.$record->ID,
false,
"deleterecord",
array('RecordID' => $record->ID)
)
->addExtraClass('gridfield-button-delete')
->setAttribute('title', _t('GridAction.Delete', "delete"))
->setAttribute('data-icon', 'decline');
return $field->Field();
}
/**
* Handle the actions and apply any changes to the GridField
*
* @param GridField $gridField
* @param string $actionName
* @param mixed $arguments
* @param array $data - form data
* @return void
*/
public function handleAction(GridField $gridField, $actionName, $arguments, $data) {
if($actionName == 'deleterecord') {
$id = $arguments['RecordID'];
// Always deletes a record. Use GridFieldRelationDelete to detach it from the current relationship.
$item = $gridField->getList()->byID($id);
if(!$item) return;
$item->delete();
}
}
}

View File

@ -0,0 +1,94 @@
<?php
/**
* Provides the entry point to editing a single record presented by the grid.
* Doesn't show an edit view on its own or modifies the record, but rather relies on routing conventions
* established in {@link getColumnContent()}. The default routing applies to
* the {@link GridFieldPopupForms} component, which has to be added separately
* to the grid field configuration.
*/
class GridFieldEditAction implements GridField_ColumnProvider {
/**
* Add a column 'Delete'
*
* @param type $gridField
* @param array $columns
*/
public function augmentColumns($gridField, &$columns) {
if(!in_array('Actions', $columns))
$columns[] = 'Actions';
}
/**
* Return any special attributes that will be used for FormField::createTag()
*
* @param GridField $gridField
* @param DataObject $record
* @param string $columnName
* @return array
*/
public function getColumnAttributes($gridField, $record, $columnName) {
return array();
}
/**
* Add the title
*
* @param GridField $gridField
* @param string $columnName
* @return array
*/
public function getColumnMetadata($gridField, $columnName) {
if($columnName == 'Actions') {
return array('title' => '');
}
}
/**
* Which columns are handled by this component
*
* @param type $gridField
* @return type
*/
public function getColumnsHandled($gridField) {
return array('Actions');
}
/**
* Which GridField actions are this component handling
*
* @param GridField $gridField
* @return array
*/
public function getActions($gridField) {
return array();
}
/**
*
* @param GridField $gridField
* @param DataObject $record
* @param string $columnName
* @return string - the HTML for the column
*/
public function getColumnContent($gridField, $record, $columnName) {
$data = new ArrayData(array(
'Link' => Controller::join_links($gridField->Link('item'), $record->ID, 'edit')
));
return $data->renderWith('GridFieldEditAction');
}
/**
* Handle the actions and apply any changes to the GridField
*
* @param GridField $gridField
* @param string $actionName
* @param mixed $arguments
* @param array $data - form data
* @return void
*/
public function handleAction(GridField $gridField, $actionName, $arguments, $data) {
}
}

View File

@ -39,13 +39,15 @@ class GridFieldExporter implements GridField_HTMLProvider, GridField_ActionProvi
* Place the export button in a <p> tag below the field * Place the export button in a <p> tag below the field
*/ */
public function getHTMLFragments($gridField) { public function getHTMLFragments($gridField) {
$button = new GridField_Action( $button = new GridField_FormAction(
$gridField, $gridField,
'export', 'export',
_t('TableListField.CSVEXPORT', 'Export to CSV'), _t('TableListField.CSVEXPORT', 'Export to CSV'),
'export', 'export',
null null
); );
$button->setAttribute('data-icon', 'download-csv');
$button->addExtraClass('no-ajax');
return array( return array(
'after' => '<p>' . $button->Field() . '</p>', 'after' => '<p>' . $button->Field() . '</p>',
); );
@ -129,6 +131,7 @@ class GridFieldExporter implements GridField_HTMLProvider, GridField_ActionProvi
*/ */
function setExportColumns($cols) { function setExportColumns($cols) {
$this->exportColumns = $cols; $this->exportColumns = $cols;
return $this;
} }
/** /**
@ -143,6 +146,7 @@ class GridFieldExporter implements GridField_HTMLProvider, GridField_ActionProvi
*/ */
function setCsvSeparator($separator) { function setCsvSeparator($separator) {
$this->csvSeparator = $separator; $this->csvSeparator = $separator;
return $this;
} }
/** /**
@ -157,6 +161,7 @@ class GridFieldExporter implements GridField_HTMLProvider, GridField_ActionProvi
*/ */
function setCsvHasHeader($bool) { function setCsvHasHeader($bool) {
$this->csvHasHeader = $bool; $this->csvHasHeader = $bool;
return $this;
} }

View File

@ -75,12 +75,14 @@ class GridFieldFilter implements GridField_HTMLProvider, GridField_DataManipulat
$field = new FieldGroup( $field = new FieldGroup(
$field, $field,
$filterbutton = new GridField_Action($gridField, 'filter', _t('GridField.Filter', "Filter"), 'filter', null), Object::create('GridField_FormAction', $gridField, 'filter', false, 'filter', null)
$resetbutton = new GridField_Action($gridField, 'reset', _t('GridField.ResetFilter', "Reset"), 'reset', null) ->addExtraClass('ss-gridfield-button-filter')
->setAttribute('title', _t('GridField.Filter', "Filter"))
,
Object::create('GridField_FormAction', $gridField, 'reset', false, 'reset', null)
->addExtraClass('ss-gridfield-button-reset')
->setAttribute('title', _t('GridField.ResetFilter', "Reset"))
); );
$filterbutton->addExtraClass('ss-gridfield-button-filter');
$resetbutton->addExtraClass('ss-gridfield-button-reset');
} else { } else {
$field = new LiteralField('', ''); $field = new LiteralField('', '');
} }

View File

@ -7,13 +7,6 @@
* @subpackage fields-relational * @subpackage fields-relational
*/ */
class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider { class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider {
/**
*
* @var int
*/
protected $currentPage = 1;
/** /**
* *
* @var int * @var int
@ -31,8 +24,8 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu
* *
* @param int $itemsPerPage - How many items should be displayed per page * @param int $itemsPerPage - How many items should be displayed per page
*/ */
public function __construct($itemsPerPage=15) { public function __construct($itemsPerPage=null) {
$this->itemsPerPage = $itemsPerPage; if($itemsPerPage) $this->itemsPerPage = $itemsPerPage;
} }
/** /**
@ -57,7 +50,7 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu
return; return;
} }
$state = $gridField->State->GridFieldPaginator; $state = $gridField->State->GridFieldPaginator;
$this->currentPage = $state->currentPage = (int)$arguments; $state->currentPage = (int)$arguments;
} }
/** /**
@ -67,13 +60,17 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu
* @return SS_List * @return SS_List
*/ */
public function getManipulatedData(GridField $gridField, SS_List $dataList) { public function getManipulatedData(GridField $gridField, SS_List $dataList) {
$state = $gridField->State->GridFieldPaginator;
if(!is_int($state->currentPage))
$state->currentPage = 1;
if(!$this->getListPaginatable($dataList)) { if(!$this->getListPaginatable($dataList)) {
return $dataList; return $dataList;
} }
if(!$this->currentPage) { if(!$state->currentPage) {
return $dataList->getRange(0, (int)$this->itemsPerPage); return $dataList->getRange(0, (int)$this->itemsPerPage);
} }
$startRow = $this->itemsPerPage*($this->currentPage-1); $startRow = $this->itemsPerPage * ($state->currentPage - 1);
return $dataList->getRange((int)$startRow, (int)$this->itemsPerPage); return $dataList->getRange((int)$startRow, (int)$this->itemsPerPage);
} }
@ -83,30 +80,85 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu
* @return array * @return array
*/ */
public function getHTMLFragments($gridField) { public function getHTMLFragments($gridField) {
$forTemplate = new ArrayData(array()); $state = $gridField->State->GridFieldPaginator;
$forTemplate->Fields = new ArrayList; if(!is_int($state->currentPage))
$state->currentPage = 1;
// Figure out which page and record range we're on
$countList = clone $gridField->List; $countList = clone $gridField->List;
$totalRows = $countList->limit(null)->count(); $totalRows = $countList->limit(null)->count();
$totalPages = ceil($totalRows/$this->itemsPerPage); if(!$totalRows) return array();
for($idx=1; $idx<=$totalPages; $idx++) {
if($idx == $this->currentPage) {
$field = new LiteralField('pagination_'.$idx, $idx);
} else {
$field = new GridField_Action($gridField, 'pagination_'.$idx, $idx, 'paginate', $idx);
$field->addExtraClass('ss-gridfield-button');
}
$forTemplate->Fields->push($field); $totalPages = ceil($totalRows/$this->itemsPerPage);
} if($totalPages == 0)
if(!$forTemplate->Fields->Count()) { $totalPages = 1;
return array(); $firstShownRecord = ($state->currentPage - 1) * $this->itemsPerPage + 1;
} if($firstShownRecord > $totalRows)
$firstShownRecord = $totalRows;
$lastShownRecord = $state->currentPage * $this->itemsPerPage;
if($lastShownRecord > $totalRows)
$lastShownRecord = $totalRows;
// First page button
$firstPage = new GridField_FormAction($gridField, 'pagination_first', 'First', 'paginate', 1);
$firstPage->addExtraClass('ss-gridfield-firstpage');
if($state->currentPage == 1)
$firstPage = $firstPage->performDisabledTransformation();
// Previous page button
$previousPageNum = $state->currentPage <= 1 ? 1 : $state->currentPage - 1;
$previousPage = new GridField_FormAction($gridField, 'pagination_prev', 'Previous', 'paginate', $previousPageNum);
$previousPage->addExtraClass('ss-gridfield-previouspage');
if($state->currentPage == 1)
$previousPage = $previousPage->performDisabledTransformation();
// Next page button
$nextPageNum = $state->currentPage >= $totalPages ? $totalPages : $state->currentPage + 1;
$nextPage = new GridField_FormAction($gridField, 'pagination_next', 'Next', 'paginate', $nextPageNum);
$nextPage->addExtraClass('ss-gridfield-nextpage');
if($state->currentPage == $totalPages)
$nextPage = $nextPage->performDisabledTransformation();
// Last page button
$lastPage = new GridField_FormAction($gridField, 'pagination_last', 'Last', 'paginate', $totalPages);
$lastPage->addExtraClass('ss-gridfield-lastpage');
if($state->currentPage == $totalPages)
$lastPage = $lastPage->performDisabledTransformation();
// Render in template
$forTemplate = new ArrayData(array(
'FirstPage' => $firstPage,
'PreviousPage' => $previousPage,
'CurrentPageNum' => $state->currentPage,
'NumPages' => $totalPages,
'NextPage' => $nextPage,
'LastPage' => $lastPage,
'FirstShownRecord' => $firstShownRecord,
'LastShownRecord' => $lastShownRecord,
'NumRecords' => $totalRows
));
return array( return array(
'footer' => $forTemplate->renderWith('GridFieldPaginator_Row', array('Colspan'=>count($gridField->getColumns()))), 'footer' => $forTemplate->renderWith('GridFieldPaginator_Row', array('Colspan'=>count($gridField->getColumns()))),
); );
} }
/**
* @param Int
*/
public function setItemsPerPage($num) {
$this->itemsPerPage = $num;
return $this;
}
/**
* @return Int
*/
public function getItemsPerPage() {
return $this->itemsPerPage;
}
/** Duck check to see if list support methods we need to paginate */ /** Duck check to see if list support methods we need to paginate */
protected function getListPaginatable(SS_List $list) { protected function getListPaginatable(SS_List $list) {
// If no list yet, not paginatable // If no list yet, not paginatable

View File

@ -3,7 +3,7 @@
/** /**
* Provides view and edit forms at GridField-specific URLs. * Provides view and edit forms at GridField-specific URLs.
* These can be placed into pop-ups by an appropriate front-end. * These can be placed into pop-ups by an appropriate front-end.
* Usually added to a grid field alongside of {@link GridFieldAction_Edit} * Usually added to a grid field alongside of {@link GridFieldEditAction}
* which takes care of linking the individual rows to their edit view. * which takes care of linking the individual rows to their edit view.
* *
* The URLs provided will be off the following form: * The URLs provided will be off the following form:
@ -25,6 +25,11 @@ class GridFieldPopupForms implements GridField_URLHandler {
*/ */
protected $name; protected $name;
/**
* @var Validator The form validator used for both add and edit fields.
*/
protected $validator;
function getURLHandlers($gridField) { function getURLHandlers($gridField) {
return array( return array(
'item/$ID' => 'handleItem', 'item/$ID' => 'handleItem',
@ -68,7 +73,7 @@ class GridFieldPopupForms implements GridField_URLHandler {
$handler = Object::create($class, $gridField, $this, $record, $controller, $this->name); $handler = Object::create($class, $gridField, $this, $record, $controller, $this->name);
$handler->setTemplate($this->template); $handler->setTemplate($this->template);
return $handler->handleRequest($request, $gridField); return $handler->handleRequest($request, DataModel::inst());
} }
/** /**
@ -76,6 +81,7 @@ class GridFieldPopupForms implements GridField_URLHandler {
*/ */
function setTemplate($template) { function setTemplate($template) {
$this->template = $template; $this->template = $template;
return $this;
} }
/** /**
@ -90,6 +96,7 @@ class GridFieldPopupForms implements GridField_URLHandler {
*/ */
function setName($name) { function setName($name) {
$this->name = $name; $this->name = $name;
return $this;
} }
/** /**
@ -98,6 +105,21 @@ class GridFieldPopupForms implements GridField_URLHandler {
function getName() { function getName() {
return $this->name; return $this->name;
} }
/**
* @param Validator $validator
*/
public function setValidator(Validator $validator) {
$this->validator = $validator;
return $this;
}
/**
* @return Validator
*/
public function getValidator() {
return $this->validator;
}
} }
class GridFieldPopupForm_ItemRequest extends RequestHandler { class GridFieldPopupForm_ItemRequest extends RequestHandler {
@ -152,7 +174,7 @@ class GridFieldPopupForm_ItemRequest extends RequestHandler {
*/ */
public function __construct($gridField, $component, $record, $popupController, $popupFormName) { public function __construct($gridField, $component, $record, $popupController, $popupFormName) {
$this->gridField = $gridField; $this->gridField = $gridField;
$this->component = $gridField; $this->component = $component;
$this->record = $record; $this->record = $record;
$this->popupController = $popupController; $this->popupController = $popupController;
$this->popupFormName = $popupFormName; $this->popupFormName = $popupFormName;
@ -164,34 +186,20 @@ class GridFieldPopupForm_ItemRequest extends RequestHandler {
} }
function edit($request) { function edit($request) {
$controller = $this->popupController; $controller = $this->getToplevelController();
$form = $this->ItemEditForm($this->gridField, $request); $form = $this->ItemEditForm($this->gridField, $request);
// TODO Coupling with CMS
if($controller instanceof LeftAndMain) {
$form->addExtraClass('cms-edit-form');
$form->setTemplate($controller->getTemplatesWithSuffix('_EditForm'));
$form->addExtraClass('cms-content center ss-tabset ' . $controller->BaseCSSClasses());
if($form->Fields()->hasTabset()) $form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet');
// TODO Link back to controller action (and edited root record) rather than index,
// which requires more URL knowledge than the current link to this field gives us.
// The current root record is held in session only,
// e.g. page/edit/show/6/ vs. page/edit/EditForm/field/MyGridField/....
$form->Backlink = $controller->Link();
}
$return = $this->customise(array( $return = $this->customise(array(
'Backlink' => $controller->Link(), 'Backlink' => $controller->Link(),
'ItemEditForm' => $form, 'ItemEditForm' => $form,
))->renderWith($this->template); ))->renderWith($this->template);
if($controller->isAjax()) { if($controller->isAjax()) {
return $return; return $return;
} else { } else {
// If not requested by ajax, we need to render it within the controller context+template // If not requested by ajax, we need to render it within the controller context+template
return $controller->customise(array( return $controller->customise(array(
// TODO Allow customization // TODO CMS coupling
'Content' => $return, 'Content' => $return,
)); ));
} }
@ -208,30 +216,84 @@ class GridFieldPopupForm_ItemRequest extends RequestHandler {
* @return Form * @return Form
*/ */
function ItemEditForm() { function ItemEditForm() {
if (empty($this->record)) {
$controller = Controller::curr();
$noActionURL = $controller->removeAction($_REQUEST['url']);
$controller->getResponse()->removeHeader('Location'); //clear the existing redirect
return Director::redirect($noActionURL, 302);
}
$actions = new FieldList();
if($this->record->ID !== 0) {
$actions->push(FormAction::create('doSave', _t('GridFieldDetailsForm.Save', 'Save'))
->setUseButtonTag(true)->addExtraClass('ss-ui-action-constructive')->setAttribute('data-icon', 'accept'));
$actions->push(FormAction::create('doDelete', _t('GridFieldDetailsForm.Delete', 'Delete'))
->addExtraClass('ss-ui-action-destructive'));
}else{ // adding new record
//Change the Save label to 'Create'
$actions->push(FormAction::create('doSave', _t('GridFieldDetailsForm.Create', 'Create'))
->setUseButtonTag(true)->addExtraClass('ss-ui-action-constructive')->setAttribute('data-icon', 'add'));
// Add a Cancel link which is a button-like link and link back to one level up.
$curmbs = $this->Breadcrumbs();
if($curmbs && $curmbs->count()>=2){
$one_level_up = $curmbs->offsetGet($curmbs->count()-2);
$text = "
<a class=\"crumb ss-ui-button ss-ui-action-destructive cms-panel-link ui-corner-all\" href=\"".$one_level_up->Link."\">
Cancel
</a>";
$actions->push(new LiteralField('cancelbutton', $text));
}
}
$form = new Form( $form = new Form(
$this, $this,
'ItemEditForm', 'ItemEditForm',
// WARNING: The arguments passed here are a little arbitrary. This API will need cleanup $this->record->getCMSFields(),
$this->record->getCMSFields($this->popupController, $this->popupFormName), $actions,
new FieldList( $this->component->getValidator()
FormAction::create('doSave', _t('GridFieldDetailsForm.Save', 'Save'))
->setUseButtonTag(true)->addExtraClass('ss-ui-action-constructive')->setAttribute('data-icon', 'accept'),
FormAction::create('doDelete', _t('GridFieldDetailsForm.Delete', 'Delete'))
->setUseButtonTag(true)->addExtraClass('ss-ui-action-destructive')
)
); );
$form->loadDataFrom($this->record); $form->loadDataFrom($this->record);
// TODO Coupling with CMS
$toplevelController = $this->getToplevelController();
if($toplevelController && $toplevelController instanceof LeftAndMain) {
// Always show with base template (full width, no other panels),
// regardless of overloaded CMS controller templates.
// TODO Allow customization, e.g. to display an edit form alongside a search form from the CMS controller
$form->setTemplate('LeftAndMain_EditForm');
$form->addExtraClass('cms-content cms-edit-form center ss-tabset');
if($form->Fields()->hasTabset()) $form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet');
// TODO Link back to controller action (and edited root record) rather than index,
// which requires more URL knowledge than the current link to this field gives us.
// The current root record is held in session only,
// e.g. page/edit/show/6/ vs. page/edit/EditForm/field/MyGridField/....
$form->Backlink = $toplevelController->Link();
}
return $form; return $form;
} }
/**
* Traverse up nested requests until we reach the first that's not a GridFieldPopupForm_ItemRequest.
* The opposite of {@link Controller::curr()}, required because
* Controller::$controller_stack is not directly accessible.
*
* @return Controller
*/
protected function getToplevelController() {
$c = $this->popupController;
while($c && $c instanceof GridFieldPopupForm_ItemRequest) {
$c = $c->getController();
}
return $c;
}
function doSave($data, $form) { function doSave($data, $form) {
$new_record = $this->record->ID == 0; $new_record = $this->record->ID == 0;
try { try {
$form->saveInto($this->record); $form->saveInto($this->record);
$this->record->write(); $this->record->write();
if($new_record) $this->gridField->getList()->add($this->record);
$this->gridField->getList()->add($this->record);
} catch(ValidationException $e) { } catch(ValidationException $e) {
$form->sessionMessage($e->getResult()->message(), 'bad'); $form->sessionMessage($e->getResult()->message(), 'bad');
return Controller::curr()->redirectBack(); return Controller::curr()->redirectBack();
@ -247,7 +309,7 @@ class GridFieldPopupForm_ItemRequest extends RequestHandler {
$form->sessionMessage($message, 'good'); $form->sessionMessage($message, 'good');
return $this->popupController->redirectBack(); return Controller::curr()->redirectBack();
} }
function doDelete($data, $form) { function doDelete($data, $form) {
@ -260,7 +322,7 @@ class GridFieldPopupForm_ItemRequest extends RequestHandler {
$toDelete->delete(); $toDelete->delete();
} catch(ValidationException $e) { } catch(ValidationException $e) {
$form->sessionMessage($e->getResult()->message(), 'bad'); $form->sessionMessage($e->getResult()->message(), 'bad');
return Director::redirectBack(); return Controller::curr()->redirectBack();
} }
$message = sprintf( $message = sprintf(
@ -283,6 +345,7 @@ class GridFieldPopupForm_ItemRequest extends RequestHandler {
*/ */
function setTemplate($template) { function setTemplate($template) {
$this->template = $template; $this->template = $template;
return $this;
} }
/** /**
@ -292,6 +355,20 @@ class GridFieldPopupForm_ItemRequest extends RequestHandler {
return $this->template; return $this->template;
} }
/**
* @return Controller
*/
function getController() {
return $this->popupController;
}
/**
* @return GridField
*/
function getGridField() {
return $this->gridField;
}
/** /**
* CMS-specific functionality: Passes through navigation breadcrumbs * CMS-specific functionality: Passes through navigation breadcrumbs
* to the template, and includes the currently edited record (if any). * to the template, and includes the currently edited record (if any).
@ -301,12 +378,17 @@ class GridFieldPopupForm_ItemRequest extends RequestHandler {
* @return ArrayData * @return ArrayData
*/ */
function Breadcrumbs($unlinked = false) { function Breadcrumbs($unlinked = false) {
if(!($this->popupController instanceof LeftAndMain)) return false; if(!$this->popupController->hasMethod('Breadcrumbs')) return;
$items = $this->popupController->Breadcrumbs($unlinked); $items = $this->popupController->Breadcrumbs($unlinked);
if($this->record) { if($this->record && $this->record->ID) {
$items->push(new ArrayData(array( $items->push(new ArrayData(array(
'Title' => $this->record->Title, 'Title' => $this->record->Title,
'Link' => $this->Link()
)));
} else {
$items->push(new ArrayData(array(
'Title' => sprintf(_t('GridField.NewRecord', 'New %s'), $this->record->singular_name()),
'Link' => false 'Link' => false
))); )));
} }

View File

@ -1,9 +1,11 @@
<?php <?php
/** /**
* A GridFieldRelationAdd is responsible for adding objects to another object's has_many and many_many relation, * This class is is responsible for adding objects to another object's has_many and many_many relation,
* as defined by the RelationList passed to the GridField constructor. * as defined by the {@link RelationList} passed to the GridField constructor.
* Objects can be searched through an input field (partially matching one or more fields). * Objects can be searched through an input field (partially matching one or more fields).
* Selecting from the results will add the object to the relation. * Selecting from the results will add the object to the relation.
* Often used alongside {@link GridFieldRelationDelete} for detaching existing records from a relatinship.
* For easier setup, have a look at a sample configuration in {@link GridFieldConfig_RelationEditor}.
*/ */
class GridFieldRelationAdd implements GridField_HTMLProvider, GridField_ActionProvider, GridField_DataManipulator, GridField_URLHandler { class GridFieldRelationAdd implements GridField_HTMLProvider, GridField_ActionProvider, GridField_DataManipulator, GridField_URLHandler {
@ -17,6 +19,8 @@ class GridFieldRelationAdd implements GridField_HTMLProvider, GridField_ActionPr
/** /**
* Which columns that should be used for doing a "StartsWith" search. * Which columns that should be used for doing a "StartsWith" search.
* If multiple fields are provided, the filtering is performed non-exclusive. * If multiple fields are provided, the filtering is performed non-exclusive.
* If no fields are provided, tries to auto-detect a "Title" or "Name" field,
* and falls back to the first textual field defined on the object.
* *
* @var Array * @var Array
*/ */
@ -36,7 +40,7 @@ class GridFieldRelationAdd implements GridField_HTMLProvider, GridField_ActionPr
* *
* @param array $searchFields Which fields on the object in the list should be searched * @param array $searchFields Which fields on the object in the list should be searched
*/ */
public function __construct($searchFields) { public function __construct($searchFields = null) {
$this->searchFields = (array)$searchFields; $this->searchFields = (array)$searchFields;
} }
@ -47,26 +51,24 @@ class GridFieldRelationAdd implements GridField_HTMLProvider, GridField_ActionPr
*/ */
public function getHTMLFragments($gridField) { public function getHTMLFragments($gridField) {
$searchState = $gridField->State->GridFieldSearchRelation; $searchState = $gridField->State->GridFieldSearchRelation;
$dataClass = $gridField->getList()->dataClass();
Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css');
Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang');
Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/jquery-ui.js');
Requirements::javascript(SAPPHIRE_DIR . "/javascript/GridFieldSearch.js");
$forTemplate = new ArrayData(array()); $forTemplate = new ArrayData(array());
$forTemplate->Fields = new ArrayList(); $forTemplate->Fields = new ArrayList();
$value = $this->findSingleEntry($gridField, $this->searchFields, $searchState, $gridField->getList()->dataClass); $searchFields = ($this->getSearchFields()) ? $this->getSearchFields() : $this->scaffoldSearchFields($dataClass);
$value = $this->findSingleEntry($gridField, $searchFields, $searchState, $dataClass);
$searchField = new TextField('gridfield_relationsearch', _t('GridField.RelationSearch', "Relation search"), $value); $searchField = new TextField('gridfield_relationsearch', _t('GridField.RelationSearch', "Relation search"), $value);
// Apparently the data-* needs to be double qouted for the jQuery.meta data plugin // Apparently the data-* needs to be double qouted for the jQuery.meta data plugin
$searchField->setAttribute('data-search-url', '\''.Controller::join_links($gridField->Link('search').'\'')); $searchField->setAttribute('data-search-url', '\''.Controller::join_links($gridField->Link('search').'\''));
$searchField->setAttribute('placeholder', $this->getPlaceholderText($gridField->getList()->dataClass())); $searchField->setAttribute('placeholder', $this->getPlaceholderText($dataClass));
$searchField->addExtraClass('relation-search'); $searchField->addExtraClass('relation-search');
$findAction = new GridField_Action($gridField, 'gridfield_relationfind', _t('GridField.Find', "Find"), 'find', 'find'); $findAction = new GridField_FormAction($gridField, 'gridfield_relationfind', _t('GridField.Find', "Find"), 'find', 'find');
$addAction = new GridField_Action($gridField, 'gridfield_relationadd', _t('GridField.LinkExisting', "Link Exisiting"), 'addto', 'addto'); $findAction->setAttribute('data-icon', 'relationfind');
$addAction = new GridField_FormAction($gridField, 'gridfield_relationadd', _t('GridField.LinkExisting', "Link Exisiting"), 'addto', 'addto');
$addAction->setAttribute('data-icon', 'chain--plus');
// If an object is not found, disable the action // If an object is not found, disable the action
if(!is_int($gridField->State->GridFieldAddRelation)) { if(!is_int($gridField->State->GridFieldAddRelation)) {
@ -151,15 +153,24 @@ class GridFieldRelationAdd implements GridField_HTMLProvider, GridField_ActionPr
* @param SS_HTTPRequest $request * @param SS_HTTPRequest $request
*/ */
public function doSearch($gridField, $request) { public function doSearch($gridField, $request) {
$allList = DataList::create($gridField->getList()->dataClass()); $dataClass = $gridField->getList()->dataClass();
$allList = DataList::create($dataClass);
$filters = array(); $filters = array();
$stmts = array(); $stmts = array();
$searchFields = ($this->getSearchFields()) ? $this->getSearchFields() : $this->scaffoldSearchFields($dataClass);
if(!$searchFields) {
throw new LogicException(
sprintf('GridFieldRelationAdd: No searchable fields could be found for class "%s"', $dataClass)
);
}
// TODO Replace with DataList->filterAny() once it correctly supports OR connectives // TODO Replace with DataList->filterAny() once it correctly supports OR connectives
foreach($this->searchFields as $searchField) { foreach($searchFields as $searchField) {
$stmts[] .= sprintf('"%s" LIKE \'%s%%\'', $searchField, $request->param('ID')); $stmts[] .= sprintf('"%s" LIKE \'%s%%\'', $searchField, $request->param('ID'));
} }
$results = $allList->where(implode(' OR ', $stmts))->subtract($gridField->getList()); $results = $allList->where(implode(' OR ', $stmts))->subtract($gridField->getList());
$results->sort($this->searchFields[0], 'ASC'); $results->sort($searchFields[0], 'ASC');
$json = array(); $json = array();
foreach($results as $result) { foreach($results as $result) {
@ -198,16 +209,35 @@ class GridFieldRelationAdd implements GridField_HTMLProvider, GridField_ActionPr
return $this->searchFields; return $this->searchFields;
} }
/**
* Detect searchable
*
* @param String
* @return Array
*/
protected function scaffoldSearchFields($dataClass) {
$obj = singleton($dataClass);
if($obj->hasDatabaseField('Title')) {
return array('Title');
} else if($obj->hasDatabaseField('Name')) {
return array('Name');
} else {
return null;
}
}
/** /**
* @param String The class of the object being searched for * @param String The class of the object being searched for
* @return String * @return String
*/ */
public function getPlaceholderText($dataClass) { public function getPlaceholderText($dataClass) {
$searchFields = ($this->getSearchFields()) ? $this->getSearchFields() : $this->scaffoldSearchFields($dataClass);
if($this->placeholderText) { if($this->placeholderText) {
return $this->placeholderText; return $this->placeholderText;
} else { } else {
$labels = array(); $labels = array();
foreach($this->searchFields as $searchField) { foreach($searchFields as $searchField) {
$label = singleton($dataClass)->fieldLabel($searchField); $label = singleton($dataClass)->fieldLabel($searchField);
if($label) $labels[] = $label; if($label) $labels[] = $label;
} }

View File

@ -1,9 +1,11 @@
<?php <?php
/** /**
* Allows to detach an item from an existing has_many or many_many relationship. * Allows to detach an item from an existing has_many or many_many relationship.
* Similar to {@link GridFieldAction_Delete}, but allows to distinguish between * Similar to {@link GridFieldDeleteAction}, but allows to distinguish between
* a "delete" and "detach" action in the UI - and to use both in parallel, if required. * a "delete" and "detach" action in the UI - and to use both in parallel, if required.
* Requires the GridField to be populated with a RelationList rather than a plain DataList. * Requires the GridField to be populated with a {@link RelationList} rather than a plain {@link DataList}.
* Often used alongside {@link GridFieldRelationAdd} to add existing records to the relationship.
* For easier setup, have a look at a sample configuration in {@link GridFieldConfig_RelationEditor}.
*/ */
class GridFieldRelationDelete implements GridField_ColumnProvider, GridField_ActionProvider { class GridFieldRelationDelete implements GridField_ColumnProvider, GridField_ActionProvider {
@ -14,7 +16,8 @@ class GridFieldRelationDelete implements GridField_ColumnProvider, GridField_Act
* @param array $columns * @param array $columns
*/ */
public function augmentColumns($gridField, &$columns) { public function augmentColumns($gridField, &$columns) {
$columns[] = 'UnlinkRelation'; if(!in_array('Actions', $columns))
$columns[] = 'Actions';
} }
/** /**
@ -37,7 +40,7 @@ class GridFieldRelationDelete implements GridField_ColumnProvider, GridField_Act
* @return array * @return array
*/ */
public function getColumnMetadata($gridField, $columnName) { public function getColumnMetadata($gridField, $columnName) {
if($columnName == 'UnlinkRelation') { if($columnName == 'Actions') {
return array('title' => ''); return array('title' => '');
} }
} }
@ -49,7 +52,7 @@ class GridFieldRelationDelete implements GridField_ColumnProvider, GridField_Act
* @return type * @return type
*/ */
public function getColumnsHandled($gridField) { public function getColumnsHandled($gridField) {
return array('UnlinkRelation'); return array('Actions');
} }
/** /**
@ -70,15 +73,17 @@ class GridFieldRelationDelete implements GridField_ColumnProvider, GridField_Act
* @return string - the HTML for the column * @return string - the HTML for the column
*/ */
public function getColumnContent($gridField, $record, $columnName) { public function getColumnContent($gridField, $record, $columnName) {
$field = new GridField_Action( $field = Object::create('GridField_FormAction',
$gridField, $gridField,
'UnlinkRelation'.$record->ID, 'UnlinkRelation'.$record->ID,
_t('GridAction.UnlinkRelation', "Unlink"), false,
"unlinkrelation", "unlinkrelation",
array('RecordID' => $record->ID) array('RecordID' => $record->ID)
); )
$output = $field->Field(); ->setAttribute('title', _t('GridAction.UnlinkRelation', "Unlink"))
return $output; ->setAttribute('data-icon', 'chain--minus')
->addExtraClass('gridfield-button-unlink');
return $field->Field();
} }
/** /**

View File

@ -28,9 +28,11 @@ class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataM
$dir = 'desc'; $dir = 'desc';
} }
$field = new GridField_Action($gridField, 'SetOrder'.$columnField, $title, "sort$dir", array('SortColumn' => $columnField)); $field = Object::create(
'GridField_FormAction', $gridField, 'SetOrder'.$columnField, $title,
"sort$dir", array('SortColumn' => $columnField)
)->addExtraClass('ss-gridfield-sort');
$field->addExtraClass('ss-gridfield-sort');
if($state->SortColumn == $columnField){ if($state->SortColumn == $columnField){
$field->addExtraClass('ss-gridfield-sorted'); $field->addExtraClass('ss-gridfield-sorted');

View File

@ -1,13 +1,47 @@
<?php <?php
/**
* Adding this class to a {@link GridFieldConfig} of a {@link GridField} adds a header title to that field.
* The header serves double duty of displaying the name of the data the GridField is showing and
* providing an "add new" button to create new object instances.
*
* The reason for making "add new" part of the title component is to make it easier for the user to tell
* which "add new" button belongs to which datagrid, in the case where multiple datagrids are on a single
* page. It is also a more efficient use of screen space.
*
* The default DataGrid includes the add button. You can hide the button by setting a boolean using the
* setNewEnabled() method
*
* @package sapphire
* @subpackage gridfield
*/
class GridFieldTitle implements GridField_HTMLProvider { class GridFieldTitle implements GridField_HTMLProvider {
protected $newEnabled = true;
function getHTMLFragments($gridField) { function getHTMLFragments($gridField) {
return array( return array(
'header' => $gridField->customise(array( 'header' => $gridField->customise(array(
'NewLink' => Controller::join_links($gridField->Link('item'), 'new') 'NewLink' => Controller::join_links($gridField->Link('item'), 'new'),
'NewEnabled' => $this->getNewEnabled()
))->renderWith('GridFieldTitle') ))->renderWith('GridFieldTitle')
); );
} }
/**
* Returns whether or not the "add new" button will appear when rendering this DataGrid title
* @return bool
*/
function getNewEnabled() {
return $this->newEnabled;
}
/**
* Enable or disable the "add new" button to add new DataGrid object instances
* @param $enabled
*/
function setNewEnabled($enabled) {
$this->newEnabled = $enabled;
}
} }
?> ?>

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 B

View File

@ -6,7 +6,10 @@
* @param {successCallback} callback to call after reloading succeeded. * @param {successCallback} callback to call after reloading succeeded.
*/ */
reload: function(ajaxOpts, successCallback) { reload: function(ajaxOpts, successCallback) {
var self = this, form = this.closest('form'), data = form.find(':input').serializeArray(); var self = this, form = this.closest('form'),
focusedElName = this.find(':input:focus').attr('name'), // Save focused element for restoring after refresh
data = form.find(':input').serializeArray();
if(!ajaxOpts) ajaxOpts = {}; if(!ajaxOpts) ajaxOpts = {};
if(!ajaxOpts.data) ajaxOpts.data = []; if(!ajaxOpts.data) ajaxOpts.data = [];
ajaxOpts.data = ajaxOpts.data.concat(data); ajaxOpts.data = ajaxOpts.data.concat(data);
@ -30,6 +33,10 @@
// of the executing method. Means that it doesn't retrigger the onmatch() on the main container. // of the executing method. Means that it doesn't retrigger the onmatch() on the main container.
self.empty().append($(data).children()); self.empty().append($(data).children());
// Refocus previously focused element. Useful e.g. for finding+adding
// multiple relationships via keyboard.
if(focusedElName) self.find(':input[name="' + focusedElName + '"]').focus();
form.removeClass('loading'); form.removeClass('loading');
if(successCallback) successCallback.apply(this, arguments); if(successCallback) successCallback.apply(this, arguments);
self.trigger('reload', self); self.trigger('reload', self);
@ -71,9 +78,9 @@
$('.ss-gridfield .ss-gridfield-item').entwine({ $('.ss-gridfield .ss-gridfield-item').entwine({
onclick: function(e) { onclick: function(e) {
if($(e.target).is('.action')) { if($(e.target).closest('.action').length) {
this._super(e); this._super(e);
return; return false;
} }
var editLink = this.find('.edit-link'); var editLink = this.find('.edit-link');
@ -94,10 +101,36 @@
} }
}); });
$('.ss-gridfield .gridfield-button-delete').entwine({ $('.ss-gridfield .action.gridfield-button-delete').entwine({
onclick: function(e){ onclick: function(e){
if(!confirm(ss.i18n._t('TABLEFIELD.DELETECONFIRMMESSAGE'))) return false; if(!confirm(ss.i18n._t('TABLEFIELD.DELETECONFIRMMESSAGE'))) {
else this._super(e); e.preventDefault();
return false;
} else {
this._super(e);
}
}
});
/**
* Prevents actions from causing an ajax reload of the field.
* Useful e.g. for actions which rely on HTTP response headers being interpreted nativel
* by the browser, like file download triggers.
*/
$('.ss-gridfield .action.no-ajax').entwine({
onclick: function(e){
var self = this, btn = this.closest(':button'), grid = this.getGridField(),
form = this.closest('form'), data = form.find(':input').serialize();
// Add current button
data += '&' + encodeURIComponent(btn.attr('name')) + '=' + encodeURIComponent(btn.val());
// Include any GET parameters from the current URL, as the view state might depend on it.
// For example, a list prefiltered through external search criteria might be passed to GridField.
if(window.location.search) data = window.location.search.replace(/^\?/, '') + '&' + data;
window.location.href = $.path.makeUrlAbsolute(grid.data('url') + '?' + data, $('base').attr('href'));
return false;
} }
}); });
@ -152,10 +185,77 @@
* rather than the whole form. * rather than the whole form.
*/ */
$('.ss-gridfield .filter-header :input').entwine({ $('.ss-gridfield .filter-header :input').entwine({
onmatch: function(){
filterbtn = this.closest('.fieldgroup').find('.ss-gridfield-button-filter');
resetbtn = this.closest('.fieldgroup').find('.ss-gridfield-button-reset');
if(this.val()) {
filterbtn.addClass('filtered');
resetbtn.addClass('filtered');
}
},
onkeydown: function(e) { onkeydown: function(e) {
filterbtn = this.closest('.fieldgroup').find('.ss-gridfield-button-filter');
resetbtn = this.closest('.fieldgroup').find('.ss-gridfield-button-reset');
if(e.keyCode == '13') { if(e.keyCode == '13') {
btn = this.closest('.filter-header').find('.ss-gridfield-button-filter'); btns = this.closest('.filter-header').find('.ss-gridfield-button-filter');
this.getGridField().reload({data: [{name: btn.attr('name'), value: btn.val()}]}); this.getGridField().reload({data: [{name: btns.attr('name'), value: btns.val()}]});
return false;
}else{
filterbtn.addClass('hover-alike');
resetbtn.addClass('hover-alike');
}
}
});
$(".ss-gridfield .relation-search").entwine({
onfocusin: function (event) {
this.autocomplete({
source: function(request, response){
var searchField = $(this.element);
var form = $(this.element).closest("form");
// Due to some very weird behaviout of jquery.metadata, the url have to be double quoted
var suggestionUrl = $(searchField).attr('data-search-url').substr(1,$(searchField).attr('data-search-url').length-2);
$.ajax({
headers: {
"X-Get-Fragment" : 'Partial'
},
type: "GET",
url: suggestionUrl+'/'+request.term,
data: form.serialize()+'&'+escape(searchField.attr('name'))+'='+escape(searchField.val()),
success: function(data) {
response( $.map(JSON.parse(data), function( name, id ) {
return { label: name, value: name, id: id };
}));
},
error: function(e) {
alert(ss.i18n._t('GRIDFIELD.ERRORINTRANSACTION', 'An error occured while fetching data from the server\n Please try again later.'));
}
});
},
select: function(event, ui) {
$(this).closest(".ss-gridfield").find("#action_gridfield_relationfind").replaceWith(
'<input type="hidden" name="relationID" value="'+ui.item.id+'" id="relationID"/>'
);
var addbutton = $(this).closest(".ss-gridfield").find("#action_gridfield_relationadd");
if(addbutton.data('button')){
addbutton.button('enable');
}else{
addbutton.removeAttr('disabled');
}
}
});
}
});
$(".ss-gridfield .pagination-page-number input").entwine({
onkeydown: function(event) {
if(event.keyCode == 13) {
var newpage = parseInt($(this).val(), 10);
var gridfield = $(this).getGridField();
gridfield.setState('GridFieldPaginator', {currentPage: newpage});
gridfield.reload();
return false; return false;
} }
} }

View File

@ -1,41 +0,0 @@
jQuery(function($){
$(document).delegate(".ss-gridfield .relation-search", "focus", function (event) {
$(this).autocomplete({
source: function(request, response){
var searchField = $(this.element);
var form = $(this.element).closest("form");
// Due to some very weird behaviout of jquery.metadata, the url have to be double quoted
var suggestionUrl = $(searchField).attr('data-search-url').substr(1,$(searchField).attr('data-search-url').length-2);
$.ajax({
headers: {
"X-Get-Fragment" : 'Partial'
},
type: "GET",
url: suggestionUrl+'/'+request.term,
data: form.serialize()+'&'+escape(searchField.attr('name'))+'='+escape(searchField.val()),
success: function(data) {
response( $.map(JSON.parse(data), function( name, id ) {
return { label: name, value: name, id: id };
}));
},
error: function(e) {
alert(ss.i18n._t('GRIDFIELD.ERRORINTRANSACTION', 'An error occured while fetching data from the server\n Please try again later.'));
}
});
},
select: function(event, ui) {
$(this).closest(".ss-gridfield").find("#action_gridfield_relationfind").replaceWith(
'<input type="hidden" name="relationID" value="'+ui.item.id+'" id="relationID"/>'
);
var addbutton = $(this).closest(".ss-gridfield").find("#action_gridfield_relationadd");
if(addbutton.data('button')){
addbutton.button('enable');
}else{
addbutton.removeAttr('disabled');
}
}
});
});
});

View File

@ -1,7 +0,0 @@
(function($) {
$('.formattingHelpText').hide();
$('.formattingHelpToggle').click(function() {
$(this).parent().find('.formattingHelpText').toggle();
return false;
})
})(jQuery);

View File

@ -8,6 +8,11 @@
* checking all boxes is purely presentational. * checking all boxes is purely presentational.
*/ */
$('.permissioncheckboxset .valADMIN input').entwine({ $('.permissioncheckboxset .valADMIN input').entwine({
onmatch: function() {
this._super();
this.toggleCheckboxes();
},
onclick: function(e) { onclick: function(e) {
this.toggleCheckboxes(); this.toggleCheckboxes();
}, },
@ -26,8 +31,8 @@
// only update attributes if previous values have been saved // only update attributes if previous values have been saved
var oldChecked = $(this).data('SecurityAdmin.oldChecked'); var oldChecked = $(this).data('SecurityAdmin.oldChecked');
var oldDisabled = $(this).data('SecurityAdmin.oldDisabled'); var oldDisabled = $(this).data('SecurityAdmin.oldDisabled');
if(oldChecked != null) $(this).attr('checked', oldChecked); if(oldChecked !== null) $(this).attr('checked', oldChecked);
if(oldDisabled != null) $(this).attr('disabled', oldDisabled); if(oldDisabled !== null) $(this).attr('disabled', oldDisabled);
}); });
} }
} }

View File

@ -306,7 +306,7 @@
$('.TreeDropdownField.multiple').entwine({ $('.TreeDropdownField.multiple').entwine({
getTreeConfig: function() { getTreeConfig: function() {
var cfg = this._super(); var cfg = this._super();
cfg.checkbox = {override_ui: true}; cfg.checkbox = {override_ui: true, two_state: true};
cfg.plugins.push('checkbox'); cfg.plugins.push('checkbox');
cfg.ui.select_limit = -1; cfg.ui.select_limit = -1;
return cfg; return cfg;

View File

@ -281,7 +281,7 @@ $lang['en_US']['Group']['Parent'] = array(
PR_MEDIUM, PR_MEDIUM,
'One group has one parent group' 'One group has one parent group'
); );
$lang['en_US']['Group']['RolesAddEditLink'] = 'Add/edit roles'; $lang['en_US']['Group']['RolesAddEditLink'] = 'Manage roles';
$lang['en_US']['Group']['SINGULARNAME'] = array( $lang['en_US']['Group']['SINGULARNAME'] = array(
'Group', 'Group',
50, 50,
@ -716,7 +716,7 @@ $lang['en_US']['Security']['PASSWORDSENTHEADER'] = 'Password reset link sent to
$lang['en_US']['Security']['PASSWORDSENTTEXT'] = 'Thank you! A reset link has been sent to \'%s\', provided an account exists for this email address.'; $lang['en_US']['Security']['PASSWORDSENTTEXT'] = 'Thank you! A reset link has been sent to \'%s\', provided an account exists for this email address.';
$lang['en_US']['SecurityAdmin']['ADDMEMBER'] = 'Add Member'; $lang['en_US']['SecurityAdmin']['ADDMEMBER'] = 'Add Member';
$lang['en_US']['SecurityAdmin']['APPLY_ROLES'] = 'Apply roles to groups'; $lang['en_US']['SecurityAdmin']['APPLY_ROLES'] = 'Apply roles to groups';
$lang['en_US']['SecurityAdmin']['APPLY_ROLES_HELP'] = 'Ability to edit the roles assigned to a group. Requires the "Access to \'Security\' section" permission.'; $lang['en_US']['SecurityAdmin']['APPLY_ROLES_HELP'] = 'Ability to edit the roles assigned to a group. Requires the "Access to \'Users\' section" permission.';
$lang['en_US']['SecurityAdmin']['EDITPERMISSIONS'] = 'Manage permissions for groups'; $lang['en_US']['SecurityAdmin']['EDITPERMISSIONS'] = 'Manage permissions for groups';
$lang['en_US']['SecurityAdmin']['EDITPERMISSIONS_HELP'] = 'Ability to edit Permissions and IP Addresses for a group. Requires the "Access to \'Security\' section" permission.'; $lang['en_US']['SecurityAdmin']['EDITPERMISSIONS_HELP'] = 'Ability to edit Permissions and IP Addresses for a group. Requires the "Access to \'Security\' section" permission.';
$lang['en_US']['SecurityAdmin']['GROUPNAME'] = 'Group name'; $lang['en_US']['SecurityAdmin']['GROUPNAME'] = 'Group name';
@ -730,7 +730,7 @@ $lang['en_US']['SecurityAdmin']['MemberListCaution'] = 'Caution: Removing member
$lang['en_US']['SecurityAdmin']['NEWGROUP'] = 'New Group'; $lang['en_US']['SecurityAdmin']['NEWGROUP'] = 'New Group';
$lang['en_US']['SecurityAdmin']['PERMISSIONS'] = 'Permissions'; $lang['en_US']['SecurityAdmin']['PERMISSIONS'] = 'Permissions';
$lang['en_US']['SecurityAdmin']['ROLES'] = 'Roles'; $lang['en_US']['SecurityAdmin']['ROLES'] = 'Roles';
$lang['en_US']['SecurityAdmin']['ROLESDESCRIPTION'] = 'This section allows you to add roles to this group. Roles are logical groupings of permissions, which can be editied in the Roles tab'; $lang['en_US']['SecurityAdmin']['ROLESDESCRIPTION'] = 'Roles are predefined sets of permissions, and can be assigned to groups.<br />They are inherited from parent groups if required.';
$lang['en_US']['SecurityAdmin']['SGROUPS'] = 'Security Groups'; $lang['en_US']['SecurityAdmin']['SGROUPS'] = 'Security Groups';
$lang['en_US']['SecurityAdmin']['TABIMPORT'] = 'Import'; $lang['en_US']['SecurityAdmin']['TABIMPORT'] = 'Import';
$lang['en_US']['SecurityAdmin']['TABROLES'] = 'Roles'; $lang['en_US']['SecurityAdmin']['TABROLES'] = 'Roles';

View File

@ -1320,7 +1320,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$result = new HasManyList($componentClass, $joinField); $result = new HasManyList($componentClass, $joinField);
if($this->model) $result->setModel($this->model); if($this->model) $result->setModel($this->model);
if($this->ID) $result->setForeignID($this->ID); $result->setForeignID($this->ID);
$result = $result->where($filter)->limit($limit)->sort($sort); $result = $result->where($filter)->limit($limit)->sort($sort);
if($join) $result = $result->join($join); if($join) $result = $result->join($join);
@ -1412,7 +1412,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
// If this is called on a singleton, then we return an 'orphaned relation' that can have the // If this is called on a singleton, then we return an 'orphaned relation' that can have the
// foreignID set elsewhere. // foreignID set elsewhere.
if($this->ID) $result->setForeignID($this->ID); $result->setForeignID($this->ID);
return $result->where($filter)->sort($sort)->limit($limit); return $result->where($filter)->sort($sort)->limit($limit);
} }

View File

@ -62,7 +62,7 @@ class DataQuery {
} }
} }
if(!$matched) user_error("Couldn't find $fieldExpression in the query filter.", E_USER_WARNING); if(!$matched) throw new InvalidArgumentException("Couldn't find $fieldExpression in the query filter.");
return $this; return $this;
} }

View File

@ -26,7 +26,7 @@ class HasManyList extends RelationList {
if(is_array($this->foreignID)) { if(is_array($this->foreignID)) {
return "\"$this->foreignKey\" IN ('" . return "\"$this->foreignKey\" IN ('" .
implode("', '", array_map('Convert::raw2sql', $this->foreignID)) . "')"; implode("', '", array_map('Convert::raw2sql', $this->foreignID)) . "')";
} else if($this->foreignID){ } else if($this->foreignID !== null){
return "\"$this->foreignKey\" = '" . return "\"$this->foreignKey\" = '" .
Convert::raw2sql($this->foreignID) . "'"; Convert::raw2sql($this->foreignID) . "'";
} }

View File

@ -73,7 +73,7 @@ class ManyManyList extends RelationList {
if(is_array($this->foreignID)) { if(is_array($this->foreignID)) {
return "\"$this->joinTable\".\"$this->foreignKey\" IN ('" . return "\"$this->joinTable\".\"$this->foreignKey\" IN ('" .
implode("', '", array_map('Convert::raw2sql', $this->foreignID)) . "')"; implode("', '", array_map('Convert::raw2sql', $this->foreignID)) . "')";
} else if($this->foreignID){ } else if($this->foreignID !== null){
return "\"$this->joinTable\".\"$this->foreignKey\" = '" . return "\"$this->joinTable\".\"$this->foreignKey\" = '" .
Convert::raw2sql($this->foreignID) . "'"; Convert::raw2sql($this->foreignID) . "'";
} }
@ -93,25 +93,24 @@ class ManyManyList extends RelationList {
if(!$this->foreignID) { if(!$this->foreignID) {
throw new Exception("ManyManyList::add() can't be called until a foreign ID is set", E_USER_WARNING); throw new Exception("ManyManyList::add() can't be called until a foreign ID is set", E_USER_WARNING);
} }
if(is_array($this->foreignID)) {
throw new Exception("ManyManyList::add() can't be called on a list linked to mulitple foreign IDs", E_USER_WARNING);
}
// Delete old entries, to prevent duplication // Delete old entries, to prevent duplication
$this->removeById($itemID); $this->removeById($itemID);
// Insert new entry // Insert new entry/entries
$manipulation = array(); foreach((array)$this->foreignID as $foreignID) {
$manipulation[$this->joinTable]['command'] = 'insert'; $manipulation = array();
$manipulation[$this->joinTable]['command'] = 'insert';
if($extraFields) foreach($extraFields as $k => $v) { if($extraFields) foreach($extraFields as $k => $v) {
$manipulation[$this->joinTable]['fields'][$k] = "'" . Convert::raw2sql($v) . "'"; $manipulation[$this->joinTable]['fields'][$k] = "'" . Convert::raw2sql($v) . "'";
}
$manipulation[$this->joinTable]['fields'][$this->localKey] = $itemID;
$manipulation[$this->joinTable]['fields'][$this->foreignKey] = $foreignID;
DB::manipulate($manipulation);
} }
$manipulation[$this->joinTable]['fields'][$this->localKey] = $itemID;
$manipulation[$this->joinTable]['fields'][$this->foreignKey] = $this->foreignID;
DB::manipulate($manipulation);
} }
/** /**

View File

@ -14,11 +14,21 @@ abstract class RelationList extends DataList {
* @param $id A single ID, or an array of IDs * @param $id A single ID, or an array of IDs
*/ */
function setForeignID($id) { function setForeignID($id) {
// If already filtered on foreign ID, remove that first
if($this->foreignID !== null) {
$oldFilter = $this->foreignIDFilter();
try {
$this->dataQuery->removeFilterOn($oldFilter);
} catch(InvalidArgumentException $e) {}
}
// Turn a 1-element array into a simple value // Turn a 1-element array into a simple value
if(is_array($id) && sizeof($id) == 1) $id = reset($id); if(is_array($id) && sizeof($id) == 1) $id = reset($id);
$this->foreignID = $id; $this->foreignID = $id;
$this->dataQuery->where($this->foreignIDFilter()); $this->dataQuery->where($this->foreignIDFilter());
return $this;
} }
/** /**

View File

@ -26,6 +26,7 @@ $gf_color_border_filter_button: #9A9A9A;
$gf_color_button: #e6e6e6; $gf_color_button: #e6e6e6;
$gf_color_button_filter_hover: #338DC1; $gf_color_button_filter_hover: #338DC1;
$gf_color_button_reset_hover: #FF0000; $gf_color_button_reset_hover: #FF0000;
$color-text-light: white;
$gf_grid_y: 12px; $gf_grid_y: 12px;
$gf_grid_x: 16px; $gf_grid_x: 16px;
@ -39,7 +40,7 @@ $gf_grid_x: 16px;
.cms { .cms {
.ss-gridfield { .ss-gridfield {
& > div { & > div {
margin-bottom: 35px; margin-bottom: $gf_grid_y*3 - 1;
} }
&[data-selectable] { &[data-selectable] {
@ -64,36 +65,44 @@ $gf_grid_x: 16px;
thead { thead {
color: darken($gf_colour_base, 50%); color: darken($gf_colour_base, 50%);
background: transparent; background: transparent;
tr.filter-header {
.fieldgroup {
max-width:$gf_grid_x*32;
}
}
} }
tbody { tbody {
background: #FFF; background: #FFF;
td { td {
/* Emulate a link by default */
button { button {
border: none; border: none;
background: none; background: none;
margin: 0 0 0 2px; margin: 0 0 0 $gf_grid_x/8;
padding: 0; padding: 0;
width: auto; width: auto;
text-shadow: none; text-shadow: none;
&.ui-state-hover { &.gridfield-button-delete.ui-state-hover {
background:none; background:none;
@include box-shadow-none;
}
&.gridfield-button-delete.ui-state-active {
border:none; border:none;
@include box-shadow-none; @include box-shadow-none;
} }
span.ui-button-text { &.gridfield-button-unlink.ui-state-hover {
text-indent:-9999em; background:none;
background: url(../images/icons/decline.png) no-repeat 0 2px; @include box-shadow-none;
padding:0; }
width:20px; &.gridfield-button-unlink.ui-state-active {
height:20px; border:none;
@include box-shadow-none;
} }
} }
a.edit-link { a.edit-link {
display:inline-block; display:inline-block;
height:20px; width:$gf_grid_x + 4;
width:20px; height:$gf_grid_y*2 - 4;
text-indent:-9999em; text-indent:-9999em;
background: url(../images/icons/document--pencil.png) no-repeat 0 1px; background: url(../images/icons/document--pencil.png) no-repeat 0 1px;
} }
@ -119,7 +128,7 @@ $gf_grid_x: 16px;
background: $gf_colour_gradient_dark; background: $gf_colour_gradient_dark;
border-top: 1px solid $gf_colour_border; border-top: 1px solid $gf_colour_border;
padding: 5px; padding: 5px;
min-height: 40px; min-height: $gf_grid_y*3 + 4;
@include background-image(linear-gradient($gf_colour_gradient_light, $gf_colour_gradient_dark)); @include background-image(linear-gradient($gf_colour_gradient_light, $gf_colour_gradient_dark));
@include border-top-radius($gf_border_radius); @include border-top-radius($gf_border_radius);
@include single-text-shadow($gf_colour_text_shadow, 0px, -1px, 0); @include single-text-shadow($gf_colour_text_shadow, 0px, -1px, 0);
@ -127,12 +136,11 @@ $gf_grid_x: 16px;
padding: 0px; padding: 0px;
font-size: $gf_grid_y*1.4; font-size: $gf_grid_y*1.4;
color:#fff; color:#fff;
margin:3px $gf_grid_x/2 0; margin:$gf_grid_y/4 $gf_grid_x/2 0;
display:inline-block; display:inline-block;
} }
.new{ .new{
font-size: $gf_grid_y*1.2; font-size: $gf_grid_y*1.2;
border-color: $gf_colour_border;
float: right; float: right;
} }
} }
@ -179,7 +187,7 @@ $gf_grid_x: 16px;
position:relative; position:relative;
} }
&.fieldgroup { &.fieldgroup {
min-width: 200px; min-width: $gf_grid_x*12.5;
padding-right:0; padding-right:0;
} }
} }
@ -201,7 +209,6 @@ $gf_grid_x: 16px;
span{ span{
@include single-text-shadow($gf_colour_text_shadow, 0px, -1px, 0); @include single-text-shadow($gf_colour_text_shadow, 0px, -1px, 0);
} }
} }
&.extra { &.extra {
background: $gf_colour_subheader; background: $gf_colour_subheader;
@ -212,7 +219,7 @@ $gf_grid_x: 16px;
display: inline; display: inline;
position: static; position: static;
input { input {
height:28px; height:$gf_grid_y*2 + 4;
} }
} }
button.ss-ui-button { button.ss-ui-button {
@ -247,7 +254,7 @@ $gf_grid_x: 16px;
border:none; border:none;
width:100%; width:100%;
text-align: left; text-align: left;
padding: 4px 0; padding: $gf_grid_y/3 0;
@include single-text-shadow($gf_colour_text_shadow, 0px, -1px, 0); @include single-text-shadow($gf_colour_text_shadow, 0px, -1px, 0);
color: #fff; color: #fff;
@include border-radius(0); @include border-radius(0);
@ -282,20 +289,20 @@ $gf_grid_x: 16px;
lighten($gf_color_button, 10%), lighten($gf_color_button, 10%),
darken($gf_color_button, 5%) darken($gf_color_button, 5%)
)); ));
&:hover { &.hover-alike:active {
@include background (url(../images/icons/filter-icons.png) no-repeat -16px 6px, @include background (url(../images/icons/filter-icons.png) no-repeat -16px 6px,
linear-gradient( linear-gradient(
lighten($gf_color_button_filter_hover, 5%), lighten($gf_color_button_filter_hover, 5%),
darken($gf_color_button_filter_hover, 5%) darken($gf_color_button_filter_hover, 5%)
)); ));
@include box-shadow(inset 0 1px 3px rgb(23, 24, 26), 0 1px 0 rgba(255, 255, 255, .6));
} }
&:active { &.hover-alike {
@include background (url(../images/icons/filter-icons.png) no-repeat -16px -17px, @include background (url(../images/icons/filter-icons.png) no-repeat -16px 6px,
linear-gradient( linear-gradient(
lighten($gf_color_button_filter_hover, 5%), lighten($gf_color_button_filter_hover, 5%),
darken($gf_color_button_filter_hover, 5%) darken($gf_color_button_filter_hover, 5%)
)); ));
@include box-shadow(inset 0 1px 3px rgb(23, 24, 26), 0 1px 0 rgba(255, 255, 255, .6));
} }
} }
&.ss-gridfield-button-reset.ss-ui-button{ &.ss-gridfield-button-reset.ss-ui-button{
@ -318,14 +325,14 @@ $gf_grid_x: 16px;
darken($gf_color_button, 5%) darken($gf_color_button, 5%)
)) ))
); );
&:hover { &.filtered:hover {
@include background (url(../images/icons/filter-icons.png) no-repeat 8px -17px, @include background (url(../images/icons/filter-icons.png) no-repeat 8px -17px,
linear-gradient( linear-gradient(
$gf_color_button_reset_hover, $gf_color_button_reset_hover,
darken($gf_color_button_reset_hover, 10%) darken($gf_color_button_reset_hover, 10%)
)); ));
} }
&:active { &.filtered:active {
@include background (url(../images/icons/filter-icons.png) no-repeat 8px -17px, @include background (url(../images/icons/filter-icons.png) no-repeat 8px -17px,
linear-gradient( linear-gradient(
$gf_color_button_reset_hover, $gf_color_button_reset_hover,
@ -347,7 +354,8 @@ $gf_grid_x: 16px;
} }
span.non-sortable { span.non-sortable {
padding:.4em 1em; padding:1em 1em;
display:block;
} }
} }
@ -358,6 +366,66 @@ $gf_grid_x: 16px;
&.bottom-all { &.bottom-all {
@include border-bottom-radius($gf_border_radius); @include border-bottom-radius($gf_border_radius);
@include background-image(linear-gradient($gf_colour_gradient_light, $gf_colour_gradient_dark)); @include background-image(linear-gradient($gf_colour_gradient_light, $gf_colour_gradient_dark));
.datagrid-pagination {
padding-top:2px;
position:absolute;
left:50%;
margin-left:$gf_grid_x*-7 - 4;
.pagination-page-number {
color:$color-text-light;
input {
width:$gf_grid_x*2 + 3;
height:$gf_grid_y*2 - 4;
margin-bottom:-$gf_grid_y/2;
padding:0px;
}
}
button.ss-gridfield-previouspage {
@include background (url(../images/icons/pagination-arrows.png) no-repeat -23px 7px);
@include box-shadow-none;
border:none;
width:$gf_grid_x - 6;
margin:0 ($gf_grid_x - 6);
span {
text-indent:-9999em;
}
}
button.ss-gridfield-nextpage {
@include background (url(../images/icons/pagination-arrows.png) no-repeat -47px 7px);
@include box-shadow-none;
border:none;
width:$gf_grid_x - 6;
margin:0 ($gf_grid_x - 6);
span {
text-indent:-9999em;
}
}
button.ss-gridfield-firstpage {
@include background (url(../images/icons/pagination-arrows.png) no-repeat 0px 7px);
@include box-shadow-none;
border:none;
width:$gf_grid_x - 6;
margin:0 ($gf_grid_x - 6);
span {
text-indent:-9999em;
}
}
button.ss-gridfield-lastpage {
@include background (url(../images/icons/pagination-arrows.png) no-repeat -73px 7px);
@include box-shadow-none;
border:none;
width:$gf_grid_x - 6;
margin:0 ($gf_grid_x - 6);
span {
text-indent:-9999em;
}
}
}
.pagination-records-number {
float:right;
padding:($gf_grid_y/2) 0;
color:$color-text-light;
}
} }
} }
&.last td { &.last td {

View File

@ -111,8 +111,8 @@ class SearchContext extends Object {
* @param string|array $sort Database column to sort on. * @param string|array $sort Database column to sort on.
* Falls back to {@link DataObject::$default_sort} if not provided. * Falls back to {@link DataObject::$default_sort} if not provided.
* @param string|array $limit * @param string|array $limit
* @param SQLQuery $existingQuery * @param DataList $existingQuery
* @return SQLQuery * @return DataList
*/ */
public function getQuery($searchParams, $sort = false, $limit = false, $existingQuery = null) { public function getQuery($searchParams, $sort = false, $limit = false, $existingQuery = null) {
if($existingQuery) { if($existingQuery) {

View File

@ -55,36 +55,26 @@ class Group extends DataObject {
/** /**
* Caution: Only call on instances, not through a singleton. * Caution: Only call on instances, not through a singleton.
* The "root group" fields will be created through {@link SecurityAdmin->EditForm()}.
* *
* @return FieldList * @return FieldList
*/ */
public function getCMSFields() { public function getCMSFields() {
Requirements::javascript(SAPPHIRE_DIR . '/javascript/PermissionCheckboxSetField.js'); Requirements::javascript(SAPPHIRE_DIR . '/javascript/PermissionCheckboxSetField.js');
$config = new GridFieldConfig();
$config->addComponent(new GridFieldTitle());
$configs = new GridFieldConfig_ManyManyEditor('FirstName', 20);
$components = $configs->getComponents();
foreach($components as $component) $config->addComponent($component);
$config->addComponents(new GridFieldExporter());
$config->getComponentByType('GridFieldRelationAdd')
->setResultsFormat('$Title ($Email)')->setSearchFields(array('FirstName', 'Surname', 'Email'));
$memberList = new GridField('Members','Members', $this->Members(), $config);
$memberList->addExtraClass('members_grid');
// @todo Implement permission checking on GridField
//$memberList->setPermissions(array('edit', 'delete', 'export', 'add', 'inlineadd'));
//$memberList->setPopupCaption(_t('SecurityAdmin.VIEWUSER', 'View User'));
$fields = new FieldList( $fields = new FieldList(
new TabSet("Root", new TabSet("Root",
new Tab('Members', _t('SecurityAdmin.MEMBERS', 'Members'), new Tab('Members', _t('SecurityAdmin.MEMBERS', 'Members'),
new TextField("Title", $this->fieldLabel('Title')), new TextField("Title", $this->fieldLabel('Title')),
$memberList $parentidfield = Object::create('DropdownField',
'ParentID',
$this->fieldLabel('Parent'),
DataList::create('Group')->exclude('ID', $this->ID)->map('ID', 'Breadcrumbs')
)->setEmptyString(' ')
), ),
$permissionsTab = new Tab('Permissions', _t('SecurityAdmin.PERMISSIONS', 'Permissions'), $permissionsTab = new Tab('Permissions', _t('SecurityAdmin.PERMISSIONS', 'Permissions'),
new PermissionCheckboxSetField( $permissionsField = new PermissionCheckboxSetField(
'Permissions', 'Permissions',
false, false,
'Permission', 'Permission',
@ -95,6 +85,24 @@ class Group extends DataObject {
) )
); );
$parentidfield->setRightTitle('<span class="aligned_right_label">' . _t('Group.GroupReminder', 'If you choose a parent group, this group will take all it\'s roles') . '</span>');
// Filter permissions
// TODO SecurityAdmin coupling, not easy to get to the form fields through GridFieldPopupForms
$permissionsField->setHiddenPermissions(SecurityAdmin::$hidden_permissions);
if($this->ID) {
$config = new GridFieldConfig_RelationEditor();
$config->addComponents(new GridFieldExporter());
$config->getComponentByType('GridFieldRelationAdd')
->setResultsFormat('$Title ($Email)')->setSearchFields(array('FirstName', 'Surname', 'Email'));
$config->getComponentByType('GridFieldPopupForms')->setValidator(new Member_Validator());
$memberList = Object::create('GridField', 'Members',false, $this->Members(), $config)->addExtraClass('members_grid');
// @todo Implement permission checking on GridField
//$memberList->setPermissions(array('edit', 'delete', 'export', 'add', 'inlineadd'));
$fields->addFieldToTab('Root.Members', $memberList);
}
// Only add a dropdown for HTML editor configurations if more than one is available. // Only add a dropdown for HTML editor configurations if more than one is available.
// Otherwise Member->getHtmlEditorConfigForCMS() will default to the 'cms' configuration. // Otherwise Member->getHtmlEditorConfigForCMS() will default to the 'cms' configuration.
$editorConfigMap = HtmlEditorConfig::get_available_configs_map(); $editorConfigMap = HtmlEditorConfig::get_available_configs_map();
@ -111,7 +119,6 @@ class Group extends DataObject {
if(!Permission::check('EDIT_PERMISSIONS')) { if(!Permission::check('EDIT_PERMISSIONS')) {
$fields->removeFieldFromTab('Root', 'Permissions'); $fields->removeFieldFromTab('Root', 'Permissions');
$fields->removeFieldFromTab('Root', 'IP Addresses');
} }
// Only show the "Roles" tab if permissions are granted to edit them, // Only show the "Roles" tab if permissions are granted to edit them,
@ -122,8 +129,16 @@ class Group extends DataObject {
new LiteralField( new LiteralField(
"", "",
"<p>" . "<p>" .
_t('SecurityAdmin.ROLESDESCRIPTION', _t(
"This section allows you to add roles to this group. Roles are logical groupings of permissions, which can be editied in the Roles tab" 'SecurityAdmin.ROLESDESCRIPTION',
"Roles are predefined sets of permissions, and can be assigned to groups.<br />They are inherited from parent groups if required."
) . '<br />' .
sprintf(
'<a href="%s" class="add-role">%s</a>',
singleton('SecurityAdmin')->Link('show/root#Root_Roles'),
// 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', 'Manage roles')
) . ) .
"</p>" "</p>"
) )
@ -131,20 +146,28 @@ class Group extends DataObject {
// Add roles (and disable all checkboxes for inherited roles) // Add roles (and disable all checkboxes for inherited roles)
$allRoles = Permission::check('ADMIN') ? DataObject::get('PermissionRole') : DataObject::get('PermissionRole', 'OnlyAdminCanApply = 0'); $allRoles = Permission::check('ADMIN') ? DataObject::get('PermissionRole') : DataObject::get('PermissionRole', 'OnlyAdminCanApply = 0');
$groupRoles = $this->Roles(); if($this->ID) {
$inheritedRoles = new ArrayList(); $groupRoles = $this->Roles();
$ancestors = $this->getAncestors(); $inheritedRoles = new ArrayList();
foreach($ancestors as $ancestor) { $ancestors = $this->getAncestors();
$ancestorRoles = $ancestor->Roles(); foreach($ancestors as $ancestor) {
if($ancestorRoles) $inheritedRoles->merge($ancestorRoles); $ancestorRoles = $ancestor->Roles();
if($ancestorRoles) $inheritedRoles->merge($ancestorRoles);
}
$groupRoleIDs = $groupRoles->column('ID') + $inheritedRoles->column('ID');
$inheritedRoleIDs = $inheritedRoles->column('ID');
} else {
$groupRoleIDs = array();
$inheritedRoleIDs = array();
} }
$fields->findOrMakeTab('Root.Roles', 'Root.' . _t('SecurityAdmin.ROLES', 'Roles'));
$fields->addFieldToTab( $rolesField = Object::create('ListboxField', 'Roles', false, $allRoles->map()->toArray())
'Root.Roles', ->setMultiple(true)
$rolesField = new CheckboxSetField('Roles', 'Roles', $allRoles) ->setDefaultItems($groupRoleIDs)
); ->setAttribute('data-placeholder', _t('Group.AddRole', 'Add a role for this group'))
$rolesField->setDefaultItems($inheritedRoles->column('ID')); ->setDisabledItems($inheritedRoleIDs);
$rolesField->setDisabledItems($inheritedRoles->column('ID')); if(!$allRoles->Count()) $rolesField->setAttribute('data-placeholder', _t('Group.NoRoles', 'No roles found'));
$fields->addFieldToTab('Root.Roles', $rolesField);
} }
$fields->push($idField = new HiddenField("ID")); $fields->push($idField = new HiddenField("ID"));
@ -185,27 +208,26 @@ class Group extends DataObject {
} }
/** /**
* Overloaded getter. * Get many-many relation to {@link Member},
* including all members which are "inherited" from children groups of this record.
* See {@link DirectMembers()} for retrieving members without any inheritance.
* *
* @TODO Where is this used, why is this overloaded? * @param String
* * @return ManyManyList
* @param $limit string SQL
* @param $offset int
* @param $filter string SQL
* @param $sort string SQL
* @param $join string SQL
* @return ComponentSet
*/ */
public function Members($filter = "", $sort = "", $join = "", $limit = "") { public function Members($filter = "", $sort = "", $join = "", $limit = "") {
// Get a DataList of the relevant groups
$groups = DataList::create("Group")->byIDs($this->collateFamilyIDs());
if($sort || $join || $limit) { if($sort || $join || $limit) {
Deprecation::notice('3.0', "The sort, join, and limit arguments are deprcated, use sort(), join() and limit() on the resulting DataList instead."); Deprecation::notice('3.0', "The sort, join, and limit arguments are deprcated, use sort(), join() and limit() on the resulting DataList instead.");
} }
// Call the relation method on the DataList to get the members from all the groups // First get direct members as a base result
$result = $groups->relation('DirectMembers')->where($filter)->sort($sort)->limit($limit); $result = $this->DirectMembers();
// Remove the default foreign key filter in prep for re-applying a filter containing all children groups.
// Filters are conjunctive in DataQuery by default, so this filter would otherwise overrule any less specific ones.
$result->dataQuery()->removeFilterOn('Group_Members');
// Now set all children groups as a new foreign key
$groups = DataList::create("Group")->byIDs($this->collateFamilyIDs());
$result = $result->forForeignID($groups->column('ID'))->where($filter)->sort($sort)->limit($limit);
if($join) $result = $result->join($join); if($join) $result = $result->join($join);
return $result; return $result;
@ -421,7 +443,6 @@ class Group extends DataObject {
$authorGroup->write(); $authorGroup->write();
Permission::grant($authorGroup->ID, 'CMS_ACCESS_CMSMain'); Permission::grant($authorGroup->ID, 'CMS_ACCESS_CMSMain');
Permission::grant($authorGroup->ID, 'CMS_ACCESS_AssetAdmin'); Permission::grant($authorGroup->ID, 'CMS_ACCESS_AssetAdmin');
Permission::grant($authorGroup->ID, 'CMS_ACCESS_CommentAdmin');
Permission::grant($authorGroup->ID, 'CMS_ACCESS_ReportAdmin'); Permission::grant($authorGroup->ID, 'CMS_ACCESS_ReportAdmin');
Permission::grant($authorGroup->ID, 'SITETREE_REORGANISE'); Permission::grant($authorGroup->ID, 'SITETREE_REORGANISE');
} }

View File

@ -939,20 +939,28 @@ class Member extends DataObject implements TemplateGlobalProvider {
/** /**
* Get a "many-to-many" map that holds for all members their group * Get a "many-to-many" map that holds for all members their group memberships,
* memberships * including any parent groups where membership is implied.
* Use {@link DirectGroups()} to only retrieve the group relations without inheritance.
* *
* @todo Push all this logic into Member_GroupSet's getIterator()? * @todo Push all this logic into Member_GroupSet's getIterator()?
*/ */
public function Groups() { public function Groups() {
$groups = new Member_GroupSet('Group', 'Group_Members', 'GroupID', 'MemberID'); $groups = new Member_GroupSet('Group', 'Group_Members', 'GroupID', 'MemberID');
if($this->ID) $groups->setForeignID($this->ID); $groups->setForeignID($this->ID);
$this->extend('updateGroups', $groups); $this->extend('updateGroups', $groups);
return $groups; return $groups;
} }
/**
* @return ManyManyList
*/
public function DirectGroups() {
return $this->getManyManyComponents('Groups');
}
/** /**
* Get member SQLMap * Get member SQLMap
@ -1138,9 +1146,8 @@ class Member extends DataObject implements TemplateGlobalProvider {
$groupsMap = DataList::create('Group')->map('ID', 'Breadcrumbs')->toArray(); $groupsMap = DataList::create('Group')->map('ID', 'Breadcrumbs')->toArray();
asort($groupsMap); asort($groupsMap);
$fields->addFieldToTab('Root.Main', $fields->addFieldToTab('Root.Main',
Object::create('CheckboxSetField', 'Groups', singleton('Group')->i18n_plural_name()) Object::create('ListboxField', 'DirectGroups', singleton('Group')->i18n_plural_name())
->setTemplate('CheckboxSetField_Select') ->setMultiple(true)->setSource($groupsMap)
->setSource($groupsMap)
); );
// Add permission field (readonly to avoid complicated group assignment logic). // Add permission field (readonly to avoid complicated group assignment logic).

View File

@ -226,6 +226,9 @@ class PermissionCheckboxSetField extends FormField {
} }
if($fieldname && $record && ($record->has_many($fieldname) || $record->many_many($fieldname))) { if($fieldname && $record && ($record->has_many($fieldname) || $record->many_many($fieldname))) {
if(!$record->ID) $record->write(); // We need a record ID to write permissions
$idList = array(); $idList = array();
if($this->value) foreach($this->value as $id => $bool) { if($this->value) foreach($this->value as $id => $bool) {
if($bool) { if($bool) {

View File

@ -10,7 +10,7 @@
<div class="ss-uploadfield-item-info"> <div class="ss-uploadfield-item-info">
<label class="ss-uploadfield-fromcomputer ss-ui-button ss-ui-action-constructive" title="<% _t('AssetUploadField.FROMCOMPUTERINFO', 'Upload from your computer') %>" data-icon="drive-upload"> <label class="ss-uploadfield-fromcomputer ss-ui-button ss-ui-action-constructive" title="<% _t('AssetUploadField.FROMCOMPUTERINFO', 'Upload from your computer') %>" data-icon="drive-upload">
<% _t('AssetUploadField.FROMCOMPUTER', 'Choose files from your computer') %> <% _t('AssetUploadField.FROMCOMPUTER', 'Choose files from your computer') %>
<input id="$id" name="$getName" class="$extraClass ss-uploadfield-fromcomputer-fileinput" data-config="$configString" type="file"<% if $multiple %> multiple="multiple"<% end_if %><% if $TabIndex %> tabindex="$TabIndex"<% end_if %> title="<% _t('AssetUploadField.FROMCOMPUTER', 'Choose files from your computer') %>" /> <input id="$id" name="$getName" class="$extraClass ss-uploadfield-fromcomputer-fileinput" data-config="$configString" type="file"<% if $multiple %> multiple="multiple"<% end_if %>> title="<% _t('AssetUploadField.FROMCOMPUTER', 'Choose files from your computer') %>" />
</label> </label>
<div class="clear"><!-- --></div> <div class="clear"><!-- --></div>

View File

@ -1,7 +1,9 @@
<tr> <tr>
<td class="bottom-all" colspan="$Colspan"> <td class="bottom-all" colspan="$Colspan">
<% control Fields %> <div class="datagrid-pagination">
$Field $FirstPage $PreviousPage <span class="pagination-page-number">Page <input class="text" value="$CurrentPageNum"/> of $NumPages</span> $NextPage $LastPage
<% end_control %> </div>
<span class="pagination-records-number">View $FirstShownRecord - $LastShownRecord of $NumRecords</span>
</td> </td>
</tr> </tr>

View File

@ -1,3 +1,3 @@
<tr class="title"> <tr class="title">
<th colspan="$ColumnCount"><h2>$Title</h2> <a href="$NewLink" class="action ss-ui-action-constructive ss-ui-button ui-button ui-widget ui-state-default ui-corner-all new new-link"><% _t('GridField.AddNew', 'Add New') %></a></th> <th colspan="$ColumnCount"><h2>$Title</h2><% if NewEnabled %> <a href="$NewLink" class="action ss-ui-action-constructive ss-ui-button ui-button ui-widget ui-state-default ui-corner-all new new-link" data-icon="add"><% _t('GridField.AddNew', 'Add New') %></a><% end_if %></th>
</tr> </tr>

View File

@ -1 +0,0 @@
<button id="$ID" class="$Class" name="$Name" type="submit" data-url="$DataURL"<% if Disabled %> disabled="disabled"<% end_if %>>$Label</button>

View File

@ -0,0 +1,3 @@
<button $AttributesHTML>
<% if ButtonContent %>$ButtonContent<% else %>$Title<% end_if %>
</button>

View File

@ -45,7 +45,7 @@
</b></label> </b></label>
<label class="ss-uploadfield-fromcomputer ss-ui-button ui-corner-all" title="<% _t('UploadField.FROMCOMPUTERINFO', 'Upload from your computer') %>" data-icon="drive-upload"> <label class="ss-uploadfield-fromcomputer ss-ui-button ui-corner-all" title="<% _t('UploadField.FROMCOMPUTERINFO', 'Upload from your computer') %>" data-icon="drive-upload">
<% _t('UploadField.FROMCOMPUTER', 'From your computer') %> <% _t('UploadField.FROMCOMPUTER', 'From your computer') %>
<input id="$id" name="$getName" class="$extraClass ss-uploadfield-fromcomputer-fileinput" data-config="$configString" type="file"<% if $multiple %> multiple="multiple"<% end_if %><% if $TabIndex %> tabindex="$TabIndex"<% end_if %> /> <input id="$id" name="$getName" class="$extraClass ss-uploadfield-fromcomputer-fileinput" data-config="$configString" type="file"<% if $multiple %> multiple="multiple"<% end_if %> />
</label> </label>
<button class="ss-uploadfield-fromfiles ss-ui-button ui-corner-all" title="<% _t('UploadField.FROMCOMPUTERINFO', 'Select from from files') %>" data-icon="network-cloud"><% _t('UploadField.FROMCOMPUTER', 'From files') %></button> <button class="ss-uploadfield-fromfiles ss-ui-button ui-corner-all" title="<% _t('UploadField.FROMCOMPUTERINFO', 'Select from from files') %>" data-icon="network-cloud"><% _t('UploadField.FROMCOMPUTER', 'From files') %></button>
<% if not $config.autoUpload %> <% if not $config.autoUpload %>

View File

@ -1,4 +1,4 @@
<ul id="$ID" class="$extraClass"> <ul id="$ID" class="$extraClass"<% if Description %> title="$Description"<% end_if %>>
<% if Options.Count %> <% if Options.Count %>
<% control Options %> <% control Options %>
<li class="$Class"> <li class="$Class">

View File

@ -1,5 +1,5 @@
<select $AttributesHTML> <select $AttributesHTML>
<% control Options %> <% control Options %>
<option value="$Value"<% if Selected %> selected<% end_if %>>$Title</option> <option value="$Value"<% if Selected %> selected="selected"<% end_if %><% if Disabled %> disabled="disabled"<% end_if %>>$Title</option>
<% end_control %> <% end_control %>
</select> </select>

View File

@ -1,4 +1,4 @@
<ul id="$ID" class="$extraClass"> <ul id="$ID" class="$extraClass"<% if Description %> title="$Description"<% end_if %>>
<% control Options %> <% control Options %>
<li class="$Class"> <li class="$Class">
<input id="$ID" class="radio" name="$Name" type="radio" value="$Value"<% if isChecked %> checked<% end_if %><% if isDisabled %> disabled<% end_if %>> <input id="$ID" class="radio" name="$Name" type="radio" value="$Value"<% if isChecked %> checked<% end_if %><% if isDisabled %> disabled<% end_if %>>

View File

@ -65,7 +65,7 @@ class CheckboxSetFieldTest extends SapphireTest {
); );
} }
function testSaveWithAssociativeArrayValueSet() { function testSaveWithArrayValueSet() {
$article = $this->objFromFixture('CheckboxSetFieldTest_Article', 'articlewithouttags'); $article = $this->objFromFixture('CheckboxSetFieldTest_Article', 'articlewithouttags');
$articleWithTags = $this->objFromFixture('CheckboxSetFieldTest_Article', 'articlewithtags'); $articleWithTags = $this->objFromFixture('CheckboxSetFieldTest_Article', 'articlewithtags');
$tag1 = $this->objFromFixture('CheckboxSetFieldTest_Tag', 'tag1'); $tag1 = $this->objFromFixture('CheckboxSetFieldTest_Tag', 'tag1');
@ -99,40 +99,6 @@ class CheckboxSetFieldTest extends SapphireTest {
); );
} }
function testSaveWithNumericArrayValueSet() {
$article = $this->objFromFixture('CheckboxSetFieldTest_Article', 'articlewithouttags');
$articleWithTags = $this->objFromFixture('CheckboxSetFieldTest_Article', 'articlewithtags');
$tag1 = $this->objFromFixture('CheckboxSetFieldTest_Tag', 'tag1');
$tag2 = $this->objFromFixture('CheckboxSetFieldTest_Tag', 'tag2');
/* Create a CheckboxSetField with 2 items selected. Note that the array is in the format (key) => (selected) */
$field = new CheckboxSetField("Tags", "Test field", DataObject::get("CheckboxSetFieldTest_Tag")->map());
$field->setValue(array(
$tag1->ID,
$tag2->ID
));
/* Saving should work */
$field->saveInto($article);
$this->assertEquals(
array($tag1->ID,$tag2->ID),
DB::query("SELECT \"CheckboxSetFieldTest_TagID\"
FROM \"CheckboxSetFieldTest_Article_Tags\"
WHERE \"CheckboxSetFieldTest_Article_Tags\".\"CheckboxSetFieldTest_ArticleID\" = $article->ID
")->column(),
'Data shold be saved into CheckboxSetField manymany relation table on the "right end"'
);
$this->assertEquals(
array($articleWithTags->ID,$article->ID),
DB::query("SELECT \"CheckboxSetFieldTest_ArticleID\"
FROM \"CheckboxSetFieldTest_Article_Tags\"
WHERE \"CheckboxSetFieldTest_Article_Tags\".\"CheckboxSetFieldTest_TagID\" = $tag1->ID
")->column(),
'Data shold be saved into CheckboxSetField manymany relation table on the "left end"'
);
}
function testLoadDataFromObject() { function testLoadDataFromObject() {
$article = $this->objFromFixture('CheckboxSetFieldTest_Article', 'articlewithouttags'); $article = $this->objFromFixture('CheckboxSetFieldTest_Article', 'articlewithouttags');
$articleWithTags = $this->objFromFixture('CheckboxSetFieldTest_Article', 'articlewithtags'); $articleWithTags = $this->objFromFixture('CheckboxSetFieldTest_Article', 'articlewithtags');

View File

@ -3,8 +3,17 @@ CheckboxSetFieldTest_Tag:
Title: Tag 1 Title: Tag 1
tag2: tag2:
Title: Tag 2 Title: Tag 2
tag3: CheckboxSetFieldTest_Article:
Title: Tag 3 articlewithouttags:
Content: Article 1
articlewithtags:
Content: Article 2
Tags: =>CheckboxSetFieldTest_Tag.tag1,=>CheckboxSetFieldTest_Tag.tag2
CheckboxSetFieldTest_Tag:
tag1:
Title: Tag 1
tag2:
Title: Tag 2
CheckboxSetFieldTest_Article: CheckboxSetFieldTest_Article:
articlewithouttags: articlewithouttags:
Content: Article 1 Content: Article 1

View File

@ -1,5 +1,5 @@
<?php <?php
class GridFieldtest extends SapphireTest { class GridFieldTest extends SapphireTest {
/** /**
* @covers GridField::__construct * @covers GridField::__construct

View File

@ -6,7 +6,49 @@
class ListboxFieldTest extends SapphireTest { class ListboxFieldTest extends SapphireTest {
protected $extraDataObjects = array('ListboxFieldTest_DataObject'); static $fixture_file = 'ListboxFieldTest.yml';
protected $extraDataObjects = array('ListboxFieldTest_DataObject', 'ListboxFieldTest_Article', 'ListboxFieldTest_Tag');
function testFieldWithManyManyRelationship() {
$articleWithTags = $this->objFromFixture('ListboxFieldTest_Article', 'articlewithtags');
$tag1 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag1');
$tag2 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag2');
$tag3 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag3');
$field = new ListboxField("Tags", "Test field", DataObject::get("ListboxFieldTest_Tag")->map()->toArray());
$field->setMultiple(true);
$field->setValue(null, $articleWithTags);
$p = new CSSContentParser($field->Field());
$tag1xml = $p->getByXpath('//option[@value=' . $tag1->ID . ']');
$tag2xml = $p->getByXpath('//option[@value=' . $tag2->ID . ']');
$tag3xml = $p->getByXpath('//option[@value=' . $tag3->ID . ']');
$this->assertEquals('selected', (string)$tag1xml[0]['selected']);
$this->assertEquals('selected', (string)$tag2xml[0]['selected']);
$this->assertNull($tag3xml[0]['selected']);
}
function testFieldWithDisabledItems() {
$articleWithTags = $this->objFromFixture('ListboxFieldTest_Article', 'articlewithtags');
$tag1 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag1');
$tag2 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag2');
$tag3 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag3');
$field = new ListboxField("Tags", "Test field", DataObject::get("ListboxFieldTest_Tag")->map()->toArray());
$field->setMultiple(true);
$field->setValue(null, $articleWithTags);
$field->setDisabledItems(array($tag1->ID, $tag3->ID));
$p = new CSSContentParser($field->Field());
$tag1xml = $p->getByXpath('//option[@value=' . $tag1->ID . ']');
$tag2xml = $p->getByXpath('//option[@value=' . $tag2->ID . ']');
$tag3xml = $p->getByXpath('//option[@value=' . $tag3->ID . ']');
$this->assertEquals('selected', (string)$tag1xml[0]['selected']);
$this->assertEquals('disabled', (string)$tag1xml[0]['disabled']);
$this->assertEquals('selected', (string)$tag2xml[0]['selected']);
$this->assertNull($tag2xml[0]['disabled']);
$this->assertNull($tag3xml[0]['selected']);
$this->assertEquals('disabled', (string)$tag3xml[0]['disabled']);
}
function testSaveIntoNullValueWithMultipleOff() { function testSaveIntoNullValueWithMultipleOff() {
$choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value'); $choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value');
@ -63,6 +105,33 @@ class ListboxFieldTest extends SapphireTest {
$this->assertEquals('a,c', $obj2->Choices); $this->assertEquals('a,c', $obj2->Choices);
} }
function testSaveIntoManyManyRelation() {
$article = $this->objFromFixture('ListboxFieldTest_Article', 'articlewithouttags');
$articleWithTags = $this->objFromFixture('ListboxFieldTest_Article', 'articlewithtags');
$tag1 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag1');
$tag2 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag2');
$field = new ListboxField("Tags", "Test field", DataObject::get("ListboxFieldTest_Tag")->map()->toArray());
$field->setMultiple(true);
// Save new relations
$field->setValue(array($tag1->ID,$tag2->ID));
$field->saveInto($article);
$article = Dataobject::get_by_id('ListboxFieldTest_Article', $article->ID, false);
$this->assertEquals(array($tag1->ID, $tag2->ID), $article->Tags()->column('ID'));
// Remove existing relation
$field->setValue(array($tag1->ID));
$field->saveInto($article);
$article = Dataobject::get_by_id('ListboxFieldTest_Article', $article->ID, false);
$this->assertEquals(array($tag1->ID), $article->Tags()->column('ID'));
// Set NULL value
$field->setValue(null);
$field->saveInto($article);
$article = Dataobject::get_by_id('ListboxFieldTest_Article', $article->ID, false);
$this->assertEquals(array(), $article->Tags()->column('ID'));
}
/** /**
* @expectedException InvalidArgumentException * @expectedException InvalidArgumentException
*/ */
@ -131,3 +200,20 @@ class ListboxFieldTest_DataObject extends DataObject implements TestOnly {
'Choices' => 'Text' 'Choices' => 'Text'
); );
} }
class ListboxFieldTest_Article extends DataObject implements TestOnly {
static $db = array(
"Content" => "Text",
);
static $many_many = array(
"Tags" => "ListboxFieldTest_Tag",
);
}
class ListboxFieldTest_Tag extends DataObject implements TestOnly {
static $belongs_many_many = array(
'Articles' => 'ListboxFieldTest_Article'
);
}

View File

@ -0,0 +1,13 @@
ListboxFieldTest_Tag:
tag1:
Title: Tag 1
tag2:
Title: Tag 2
tag3:
Title: Tag 3
ListboxFieldTest_Article:
articlewithouttags:
Content: Article 1
articlewithtags:
Content: Article 2
Tags: =>ListboxFieldTest_Tag.tag1,=>ListboxFieldTest_Tag.tag2

View File

@ -82,6 +82,37 @@ class GridFieldConfigTest extends SapphireTest {
); );
} }
public function testRemoveComponents() {
$config = GridFieldConfig::create()
->addComponent($c1 = new GridFieldConfigTest_MyComponent())
->addComponent($c2 = new GridFieldConfigTest_MyComponent())
->addComponent($c3 = new GridFieldConfigTest_MyOtherComponent())
->addComponent($c4 = new GridFieldConfigTest_MyOtherComponent());
$this->assertEquals(
4,
$config->getComponents()->count()
);
$config->removeComponent($c1);
$this->assertEquals(
3,
$config->getComponents()->count()
);
$config->removeComponentsByType("GridFieldConfigTest_MyComponent");
$this->assertEquals(
2,
$config->getComponents()->count()
);
$config->removeComponentsByType("GridFieldConfigTest_MyOtherComponent");
$this->assertEquals(
0,
$config->getComponents()->count()
);
}
} }
class GridFieldConfigTest_MyComponent implements GridField_URLHandler, TestOnly { class GridFieldConfigTest_MyComponent implements GridField_URLHandler, TestOnly {

View File

@ -0,0 +1,121 @@
<?php
class GridFieldPopupFormsTest extends FunctionalTest {
static $fixture_file = 'GridFieldPopupFormsTest.yml';
protected $extraDataObjects = array(
'GridFieldPopupFormsTest_Person',
'GridFieldPopupFormsTest_PeopleGroup'
);
function testAddForm() {
$group = DataList::create('GridFieldPopupFormsTest_PeopleGroup')
->filter('Name', 'My Group')
->First();
$count = $group->People()->Count();
$response = $this->get('GridFieldPopupFormsTest_Controller');
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$addlinkitem = $parser->getBySelector('.ss-gridfield .new-link');
$addlink = (string) $addlinkitem[0]['href'];
$response = $this->get($addlink);
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$addform = $parser->getBySelector('#Form_ItemEditForm');
$addformurl = (string) $addform[0]['action'];
$response = $this->post(
$addformurl,
array(
'FirstName' => 'Jeremiah',
'Surname' => 'BullFrog',
'action_doSave' => 1
)
);
$this->assertFalse($response->isError());
$group = DataList::create('GridFieldPopupFormsTest_PeopleGroup')
->filter('Name', 'My Group')
->First();
$this->assertEquals($count + 1, $group->People()->Count());
}
function testEditForm() {
$group = DataList::create('GridFieldPopupFormsTest_PeopleGroup')
->filter('Name', 'My Group')
->First();
$firstperson = $group->People()->First();
$this->assertTrue($firstperson->Surname != 'Baggins');
$response = $this->get('GridFieldPopupFormsTest_Controller');
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$editlinkitem = $parser->getBySelector('.ss-gridfield-items .first .edit-link');
$editlink = (string) $editlinkitem[0]['href'];
$response = $this->get($editlink);
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$editform = $parser->getBySelector('#Form_ItemEditForm');
$editformurl = (string) $editform[0]['action'];
$response = $this->post(
$editformurl,
array(
'FirstName' => 'Bilbo',
'Surname' => 'Baggins',
'action_doSave' => 1
)
);
$this->assertFalse($response->isError());
$group = DataList::create('GridFieldPopupFormsTest_PeopleGroup')
->filter('Name', 'My Group')
->First();
$firstperson = $group->People()->First();
$this->assertEquals($firstperson->Surname, 'Baggins');
}
}
class GridFieldPopupFormsTest_Person extends DataObject implements TestOnly {
static $db = array(
'FirstName' => 'Varchar',
'Surname' => 'Varchar'
);
static $has_one = array(
'Group' => 'GridFieldPopupFormsTest_PeopleGroup'
);
}
class GridFieldPopupFormsTest_PeopleGroup extends DataObject implements TestOnly {
static $db = array(
'Name' => 'Varchar'
);
static $has_many = array(
'People' => 'GridFieldPopupFormsTest_Person'
);
}
class GridFieldPopupFormsTest_Controller extends Controller implements TestOnly {
protected $template = 'BlankPage';
function Form() {
$group = DataList::create('GridFieldPopupFormsTest_PeopleGroup')
->filter('Name', 'My Group')
->First();
$field = new GridField('testfield', 'testfield', $group->People());
$field->getConfig()->addComponent($gridFieldForm = new GridFieldPopupForms($this, 'Form'));
$field->getConfig()->addComponent(new GridFieldEditAction());
return new Form($this, 'Form', new FieldList($field), new FieldList());
}
}
?>

View File

@ -0,0 +1,12 @@
GridFieldPopupFormsTest_Person:
joe:
FirstName: Joe
Surname: Bloggs
jane:
FirstName: Jane
Surname: Doe
GridFieldPopupFormsTest_PeopleGroup:
group:
Name: My Group
People: =>GridFieldPopupFormsTest_Person.joe,=>GridFieldPopupFormsTest_Person.jane

View File

@ -0,0 +1,33 @@
<?php
class GridFieldTitleTest extends SapphireTest {
public function testGridTitleAddNewEnabled() {
//construct a fake form field to render out the grid field within it
$config = new GridFieldConfig();
$config->addComponent($titleField = new GridFieldTitle());
$actions = new FieldList();
$grid = new GridField('TestField', 'Test Field', new DataList('Company'),$config);
$fields = new FieldList($rootTab = new TabSet("Root",$tabMain = new Tab('Main',$grid)));
$form = new Form(Controller::curr(), "TestForm", $fields, $actions);
$titleField->setNewEnabled(true);
$html = $form->forTemplate();
$this->assertContains('data-icon="add"', $html,"HTML contains the 'add new' button");
}
public function testGridTitleAddNewDisabled() {
//construct a fake form field to render out the grid field within it
$config = new GridFieldConfig();
$config->addComponent($titleField = new GridFieldTitle());
$actions = new FieldList();
$grid = new GridField('TestField', 'Test Field', new DataList('Company'),$config);
$fields = new FieldList($rootTab = new TabSet("Root",$tabMain = new Tab('Main',$grid)));
$form = new Form(Controller::curr(), "TestForm", $fields, $actions);
$titleField->setNewEnabled(false);
$html = $form->forTemplate();
$this->assertNotContains('data-icon="add"', $html,"HTML does not contain the 'add new' button");
}
}
?>

View File

@ -1,4 +1,4 @@
<?php <?php
/** /**
* Test the API for creating GridField_URLHandler compeonnts * Test the API for creating GridField_URLHandler compeonnts

View File

@ -3,7 +3,8 @@ DataObjectTest_Team:
Title: Team 1 Title: Team 1
team2: team2:
Title: Team 2 Title: Team 2
team3:
Title: Team 3
DataObjectTest_Player: DataObjectTest_Player:
captain1: captain1:
FirstName: Captain FirstName: Captain

View File

@ -0,0 +1,20 @@
<?php
class HasManyListTest extends SapphireTest {
// Borrow the model from DataObjectTest
public static $fixture_file = 'DataObjectTest.yml';
protected $extraDataObjects = array(
'DataObjectTest_Team',
'DataObjectTest_SubTeam',
'DataObjectTest_Player',
);
public function testRelationshipEmptyOnNewRecords() {
// Relies on the fact that (unrelated) comments exist in the fixture file already
$newTeam = new DataObjectTest_Team(); // has_many Comments
$this->assertEquals(array(), $newTeam->Comments()->column('ID'));
}
}

View File

@ -16,6 +16,12 @@ class ManyManyListTest extends SapphireTest {
$this->assertEquals(2, $list->count()); $this->assertEquals(2, $list->count());
} }
public function testRelationshipEmptyOnNewRecords() {
// Relies on the fact that (unrelated) teams exist in the fixture file already
$newPlayer = new DataObjectTest_Player(); // many_many Teams
$this->assertEquals(array(), $newPlayer->Teams()->column('ID'));
}
public function testAddingSingleDataObjectByReference() { public function testAddingSingleDataObjectByReference() {
$player1 = $this->objFromFixture('DataObjectTest_Player', 'player1'); $player1 = $this->objFromFixture('DataObjectTest_Player', 'player1');
$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1'); $team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
@ -73,6 +79,20 @@ class ManyManyListTest extends SapphireTest {
$this->assertEquals(array($team2->ID), $player1->Teams()->column()); $this->assertEquals(array($team2->ID), $player1->Teams()->column());
} }
public function testAddingWithMultipleForeignKeys() {
$newPlayer = new DataObjectTest_Player();
$newPlayer->write();
$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
$team2 = $this->objFromFixture('DataObjectTest_Team', 'team2');
$playersTeam1Team2 = DataList::create('DataObjectTest_Team')->relation('Players')->setForeignID(array($team1->ID, $team2->ID));
$playersTeam1Team2->add($newPlayer);
$this->assertEquals(
array($team1->ID, $team2->ID),
$newPlayer->Teams()->column('ID')
);
}
public function testSubtractOnAManyManyList() { public function testSubtractOnAManyManyList() {
$allList = ManyManyList::create('DataObjectTest_Player', 'DataObjectTest_Team_Players','DataObjectTest_PlayerID', 'DataObjectTest_TeamID'); $allList = ManyManyList::create('DataObjectTest_Player', 'DataObjectTest_Team_Players','DataObjectTest_PlayerID', 'DataObjectTest_TeamID');
$this->assertEquals(3, $allList->count(), 'Precondition; we have all 3 players connected to a team in the list'); $this->assertEquals(3, $allList->count(), 'Precondition; we have all 3 players connected to a team in the list');