From 77337ae58c66c954caad0028db5acd79c7bcde6b Mon Sep 17 00:00:00 2001 From: Sean Harvey Date: Fri, 16 Nov 2012 11:04:28 +1300 Subject: [PATCH] Removing deprecated TableListField and subclasses These have been moved to a module called "legacytablefields" located at https://github.com/silverstripe-labs/legacytablefields --- css/ComplexTableField.css | 6 - css/HasManyFileField.css | 30 - css/TableListField.css | 76 - forms/ComplexTableField.php | 883 ----------- forms/HasManyComplexTableField.php | 181 --- forms/HasOneComplexTableField.php | 109 -- forms/ManyManyComplexTableField.php | 116 -- forms/TableField.php | 724 --------- forms/TableListField.php | 1644 --------------------- javascript/ComplexTableField.js | 156 -- javascript/ComplexTableField_popup.js | 11 - javascript/HasManyFileField.js | 137 -- javascript/RelationComplexTableField.js | 117 -- javascript/TableField.js | 191 --- javascript/TableListField.js | 374 ----- lang/en.yml | 41 - scss/ComplexTableField.scss | 16 - scss/HasManyFileField.scss | 124 -- scss/TableListField.scss | 226 --- templates/ComplexTableField.ss | 82 - templates/ComplexTableField_popup.ss | 42 - templates/RelationComplexTableField.ss | 67 - templates/TableField.ss | 71 - templates/TableListField.ss | 85 -- templates/TableListField_printable.ss | 12 - tests/forms/ComplexTableFieldTest.php | 202 --- tests/forms/ComplexTableFieldTest.yml | 16 - tests/forms/TableFieldTest.php | 263 ---- tests/forms/TableFieldTest.yml | 13 - tests/forms/TableListFieldTest.php | 375 ----- tests/forms/TableListFieldTest.yml | 31 - thirdparty/scriptaculous/builder.js | 101 -- thirdparty/scriptaculous/controls.js | 750 ---------- thirdparty/scriptaculous/dragdrop.js | 581 -------- thirdparty/scriptaculous/effects.js | 904 ----------- thirdparty/scriptaculous/scriptaculous.js | 48 - thirdparty/scriptaculous/slider.js | 275 ---- thirdparty/scriptaculous/unittest.js | 363 ----- 38 files changed, 9443 deletions(-) delete mode 100644 css/ComplexTableField.css delete mode 100644 css/HasManyFileField.css delete mode 100644 css/TableListField.css delete mode 100644 forms/ComplexTableField.php delete mode 100644 forms/HasManyComplexTableField.php delete mode 100644 forms/HasOneComplexTableField.php delete mode 100644 forms/ManyManyComplexTableField.php delete mode 100644 forms/TableField.php delete mode 100644 forms/TableListField.php delete mode 100644 javascript/ComplexTableField.js delete mode 100644 javascript/ComplexTableField_popup.js delete mode 100644 javascript/HasManyFileField.js delete mode 100644 javascript/RelationComplexTableField.js delete mode 100644 javascript/TableField.js delete mode 100644 javascript/TableListField.js delete mode 100755 scss/ComplexTableField.scss delete mode 100755 scss/HasManyFileField.scss delete mode 100755 scss/TableListField.scss delete mode 100644 templates/ComplexTableField.ss delete mode 100644 templates/ComplexTableField_popup.ss delete mode 100644 templates/RelationComplexTableField.ss delete mode 100644 templates/TableField.ss delete mode 100644 templates/TableListField.ss delete mode 100644 templates/TableListField_printable.ss delete mode 100644 tests/forms/ComplexTableFieldTest.php delete mode 100644 tests/forms/ComplexTableFieldTest.yml delete mode 100644 tests/forms/TableFieldTest.php delete mode 100644 tests/forms/TableFieldTest.yml delete mode 100644 tests/forms/TableListFieldTest.php delete mode 100644 tests/forms/TableListFieldTest.yml delete mode 100644 thirdparty/scriptaculous/builder.js delete mode 100644 thirdparty/scriptaculous/controls.js delete mode 100644 thirdparty/scriptaculous/dragdrop.js delete mode 100644 thirdparty/scriptaculous/effects.js delete mode 100644 thirdparty/scriptaculous/scriptaculous.js delete mode 100644 thirdparty/scriptaculous/slider.js delete mode 100644 thirdparty/scriptaculous/unittest.js diff --git a/css/ComplexTableField.css b/css/ComplexTableField.css deleted file mode 100644 index d5805ecbe..000000000 --- a/css/ComplexTableField.css +++ /dev/null @@ -1,6 +0,0 @@ -/* table */ -.ComplexTableField { margin-bottom: 10px; } -.ComplexTableField tbody td { cursor: pointer; } -.ComplexTableField tbody td.markingcheckbox { cursor: default; } - -.ui-dialog .ctf-dialog.ui-dialog-content { padding-right: 0; /* scrollbars */ } diff --git a/css/HasManyFileField.css b/css/HasManyFileField.css deleted file mode 100644 index e87787835..000000000 --- a/css/HasManyFileField.css +++ /dev/null @@ -1,30 +0,0 @@ -#right form .hasmanyfile a.addFile, #right form .hasmanyfile a.uploadFile, #right form .hasmanyfile .currentFiles li { font-size: 1.2em; padding-left: 3px; } - -#right form .hasmanyfile a.uploadFile { border-color: #cccccc #999999 #999999 #cccccc; border-style: solid; border-width: 2px; color: #333333; cursor: pointer; font-size: 11px; font-weight: bold; position: relative; top: -29px; left: 344px; text-decoration: none; overflow: visible; padding: 3px 5px; float: left; width: auto; } - -#right form .hasmanyfile a.removeFile { border-color: #cccccc #999999 #999999 #cccccc; border-style: solid; border-width: 2px; color: #333333; cursor: pointer; font-size: 10px; font-weight: bold; text-decoration: none; overflow: visible; padding: 3px 5px; width: auto; margin: -2px 0 0 10px; } - -#right form .hasmanyfile a.removeFile:hover { background: #CE0000; color: #fff; } - -#right form .hasmanyfile a.addFile { border-color: #cccccc #999999 #999999 #cccccc; border-style: solid; border-width: 2px; color: #333333; cursor: pointer; font-size: 11px; font-weight: bold; position: relative; top: -29px; left: 340px; text-decoration: none; overflow: visible; padding: 3px 5px; float: left; width: auto; } - -#right form .hasmanyfile a.addFile:hover, #right form .hasmanyfile a.uploadFile:hover { background: #fff; } - -#right form .hasmanyfile ul.currentFiles { padding-bottom: 5px; } - -#right form .hasmanyfile .currentFiles li { height: 30px; line-height: 30px; } - -#right form .hasmanyfile .clear { clear: both; } - -/* ICONS */ -#right form .hasmanyfile .currentFiles a[href$=".pdf"], #right form .hasmanyfile .currentFiles a[href$=".PDF"], #right form .hasmanyfile .currentFiles a.pdf { padding: 2px; padding-left: 20px; background: url(../images/icons/page_white_acrobat.png) no-repeat left center; } - -#right form .hasmanyfile .currentFiles a[href$=".doc"], #right form .hasmanyfile .currentFiles a[href$=".DOC"], #right form .hasmanyfile .currentFiles a.doc { padding: 2px; padding-left: 20px; background: url(../images/icons/page_word.png) no-repeat left center; } - -#right form .hasmanyfile .currentFiles a[href$=".xls"], #right form .hasmanyfile .currentFiles a[href$=".XLS"], #right form .hasmanyfile .currentFiles a.xls { padding: 2px; padding-left: 20px; background: url(../images/icons/page_excel.png) no-repeat left center; } - -#right form .hasmanyfile .currentFiles a[href$=".gz"], #right form .hasmanyfile .currentFiles a[href$=".GZ"], #right form .hasmanyfile .currentFiles a[href$=".gzip"], #right form .hasmanyfile .currentFiles a[href$=".GZIP"], #right form .hasmanyfile .currentFiles a[href$=".zip"], #right form .hasmanyfile .currentFiles a[href$=".ZIP"], #right form .hasmanyfile .currentFiles a.archive { padding: 2px; padding-left: 20px; background: url(../images/icons/page_white_zip.png) no-repeat left center; } - -#right form .hasmanyfile .currentFiles a[href$=".jpg"], #right form .hasmanyfile .currentFiles a[href$=".JPG"], #right form .hasmanyfile .currentFiles a[href$=".gif"], #right form .hasmanyfile .currentFiles a[href$=".GIF"], #right form .hasmanyfile .currentFiles a[href$=".png"], #right form .hasmanyfile .currentFiles a[href$=".PNG"], #right form .hasmanyfile .currentFiles a.image { padding: 2px; padding-left: 20px; background: url(../images/icons/icon-jpg.gif) no-repeat left center; } - -#right form .hasmanyfile .currentFiles a[href$=".exe"], #right form .hasmanyfile .currentFiles a[href$=".EXE"], #right form .hasmanyfile .currentFiles a.application { padding: 2px; padding-left: 20px; background: url(../images/icons/application.png) no-repeat left center; } diff --git a/css/TableListField.css b/css/TableListField.css deleted file mode 100644 index 038648cf4..000000000 --- a/css/TableListField.css +++ /dev/null @@ -1,76 +0,0 @@ -table.TableField, table.TableListField, .TableListField table.data, table.CMSList { border-collapse: collapse; border-spacing: 0; width: 100%; } - -/* Preventing IE6 from showing double borders */ -body > div table.TableField, body > div table.TableListField, body > div .TableListField table.data, body > div table.CMSList { border-collapse: separate; } - -table.TableField td, table.TableListField td, .TableListField table.data td, table.CMSList td { border-style: none; } - -table.TableField th, table.TableListField th, .TableListField table.data th, table.CMSList th { white-space: nowrap; } - -table.TableField thead th, .TableListField table.data thead th, table.CMSList thead th { white-space: nowrap; padding: 3px; font-size: 12px; text-align: left; } - -table.TableField thead th span, .TableListField table.data thead th span { font-size: 12px; } - -table.TableField thead th span.sortLink, .TableListField table.data thead th span.sortLink, table.CMSList thead th span.sortLink { overflow: hidden; } - -table.TableField tbody td, table.TableField tfoot td, .TableListField table.data tbody td, .TableListField table.data tfoot td, table.CMSList tbody td, table.CMSList tfoot td { padding: 2px 4px; } - -.TableListField table.data tfoot tr.addtogrouprow td { padding: 3px; } - -.TableListField table.data tfoot .actions { float: none; } - -.TableListField table.data tfoot tr.addtogrouprow input { width: 94%; } - -.TableField td input, .TableListField td input { width: 98%; } - -table.data tbody td input, table.data tbody td textarea { border: 0 !important; } - -table.TableField tbody td.checkbox, .TableListField table.data tbody td.checkbox, table.CMSList tbody td.checkbox { padding-left: 5px; background-image: url(../images/checkbox.png); background-repeat: repeat-x; background-position: left bottom; } - -.TableListField table.data tfoot .addlink img { vertical-align: middle; margin: 3px 6px 3px 3px; } - -.TableListField table.data tfoot tr td a { text-decoration: none; } - -.TableListField table.data tbody tr td a:hover, .TableListField table.data tfoot tr td a:hover { background: none; } - -/** Show a loading indication on a TableListField row */ -.TableListField tr.loading td.first { padding-left: 22px; background: url(../images/network-save.gif) 3px 2px no-repeat; } - -.right form .TableField span.readonly { border: 0; background: none; padding: 0; margin-bottom: 0; } - -.right form .TableListField td { background: #fff; } - -.TableListField div.utility { overflow: auto; } - -.TableListField div.utility .item { margin-top: 1em; padding: 3px 0 6px 0; display: block; float: left; } - -.TableListField div.utility a { text-decoration: none; color: #333; cursor: pointer; font-size: 11px; margin-right: 2px; overflow: visible; padding: 3px 2px; width: auto; } - -form .TableField .message { width: auto; } - -.TableListField .selectOptions { overflow: auto; font: 1.3em; margin: 0; padding: 0; } - -.TableListField .selectOptions li { float: left; margin: 0px 5px; } - -.TableListField .PageControls { margin: 5px 0; text-align: center; display: block; margin-bottom: 5px; position: relative; } - -.TableListField .PageControls * { display: inline; vertical-align: middle; font-weight: bold; } - -.TableListField .PageControls .Last { display: block; width: 40px; text-align: right; position: absolute; right: 0px; top: 0px; } - -.TableListField .PageControls .First { float: left; display: block; width: 40px; text-align: left; } - -#Pagination { margin-top: 10px; margin-left: auto; margin-right: auto; } -#Pagination a { font-size: 14px; width: 1px; height: 1px; margin: 1px; } -#Pagination span { display: inline; font-size: 14px; } -#Pagination div { display: inline; } - -#Pagination_Next a { text-decoration: none; } - -#Pagination_Next a div { position: relative; left: -20px; } - -#Pagination_Next a img { position: relative; top: -15px; left: 5px; } - -#Pagination_Previous a { text-decoration: none; } - -#Pagination_Previous a img { position: relative; top: -15px; left: 35px; } diff --git a/forms/ComplexTableField.php b/forms/ComplexTableField.php deleted file mode 100644 index 2cbc9ff81..000000000 --- a/forms/ComplexTableField.php +++ /dev/null @@ -1,883 +0,0 @@ - array( - 'label' => 'Show', - 'icon' => 'framework/images/show.png', - 'icon_disabled' => 'framework/images/show_disabled.png', - 'class' => 'popuplink showlink', - ), - 'edit' => array( - 'label' => 'Edit', - 'icon' => 'framework/images/edit.gif', - 'icon_disabled' => 'framework/images/edit_disabled.gif', - 'class' => 'popuplink editlink', - ), - 'delete' => array( - 'label' => 'Delete', - 'icon' => 'framework/images/delete.gif', - 'icon_disabled' => 'framework/images/delete_disabled.gif', - 'class' => 'popuplink deletelink', - ), - ); - - static $url_handlers = array( - 'item/$ID' => 'handleItem', - '$Action!' => '$Action', - ); - - public function handleItem($request) { - return new ComplexTableField_ItemRequest($this, $request->param('ID')); - } - - public function getViewer() { - return new SSViewer($this->template); - } - - public function setPopupSize($width, $height) { - $width = (int)$width; - $height = (int)$height; - - if($width < 0 || $height < 0) { - user_error("setPopupSize expects non-negative arguments.", E_USER_WARNING); - return; - } - - $this->popupWidth = $width; - $this->popupHeight = $height; - } - - public function PopupWidth() { - return $this->popupWidth; - } - - public function PopupHeight() { - return $this->popupHeight; - } - - /** - * See class comments - * - * @param Controller $controller - * @param string $name - * @param string $sourceClass - * @param array $fieldList - * @param FieldList $detailFormFields - * @param string $sourceFilter - * @param string $sourceSort - * @param string $sourceJoin - */ - public function __construct($controller, $name, $sourceClass, $fieldList = null, $detailFormFields = null, - $sourceFilter = "", $sourceSort = "", $sourceJoin = "") { - - $this->detailFormFields = $detailFormFields; - $this->controller = $controller; - $this->pageSize = 10; - - parent::__construct($name, $sourceClass, $fieldList, $sourceFilter, $sourceSort, $sourceJoin); - } - - public function isComposite() { - return false; - } - - /** - * @return String - */ - public function FieldHolder($properties = array()) { - Requirements::javascript(THIRDPARTY_DIR . "/prototype/prototype.js"); - Requirements::javascript(THIRDPARTY_DIR . "/behaviour/behaviour.js"); - Requirements::javascript(THIRDPARTY_DIR . "/greybox/AmiJS.js"); - Requirements::javascript(THIRDPARTY_DIR . "/greybox/greybox.js"); - Requirements::add_i18n_javascript(FRAMEWORK_DIR . '/javascript/lang'); - Requirements::javascript(FRAMEWORK_DIR . '/javascript/TableListField.js'); - Requirements::javascript(FRAMEWORK_DIR . "/javascript/ComplexTableField.js"); - Requirements::css(THIRDPARTY_DIR . "/greybox/greybox.css"); - Requirements::css(FRAMEWORK_DIR . "/css/TableListField.css"); - Requirements::css(FRAMEWORK_DIR . "/css/ComplexTableField.css"); - - // set caption if required - if($this->popupCaption) { - $id = $this->id(); - if(Director::is_ajax()) { - $js = <<sourceItems(); - - return $this->renderWith($this->template); - } - - /** - * @return SS_List - */ - public function Items() { - $sourceItems = $this->sourceItems(); - - if(!$sourceItems) { - return null; - } - - - if(isset($_REQUEST['ctf'][$this->getName()]['start'])) { - $pageStart = $_REQUEST['ctf'][$this->getName()]['start']; - if(!is_numeric($pageStart)) $pageStart = 0; - } else { - $pageStart = 0; - } - - $output = new ArrayList(); - foreach($sourceItems as $pageIndex=>$item) { - $output->push(Object::create($this->itemClass,$item, $this, $pageStart+$pageIndex)); - } - return $output; - } - - /** - * Sets the popup-title by javascript. Make sure to use FormResponse in ajax-requests, - * otherwise the title-change will only take effect on items existing during page-load. - * - * @param $caption String - */ - public function setPopupCaption($caption) { - $this->popupCaption = Convert::raw2js($caption); - } - - /** - * @param $validator Validator - */ - public function setDetailFormValidator( Validator $validator ) { - $this->detailFormValidator = $validator; - } - - public function setAddTitle($addTitle) { - if(is_string($addTitle)) - $this->addTitle = $addTitle; - } - - public function Title() { - return $this->addTitle ? $this->addTitle : parent::Title(); - } - - /** - * Calculates the number of columns needed for colspans - * used in template - * - * @return Int - */ - public function ItemCount() { - return count($this->fieldList); - } - - /** - * Used to toggle paging (makes no sense when adding a record) - * - * @return Boolean - */ - public function IsAddMode() { - return ($this->methodName == "add" || $this->request->param('Action') == 'AddForm'); - } - - public function sourceID() { - $idField = $this->form->Fields()->dataFieldByName('ID'); - - // disabled as it conflicts with scaffolded formfields, and not strictly necessary - // if(!$idField) user_error("ComplexTableField needs a formfield named 'ID' to be present", E_USER_ERROR); - - // because action_callfieldmethod never actually loads data into the form, - // we can't rely on $idField being populated, and fall back to the request-params. - // this is a workaround for a bug where each subsequent popup-call didn't have ID - // of the parent set, and so didn't properly save the relation - return ($idField) ? $idField->Value() : (isset($_REQUEST['ctf']['ID']) ? $_REQUEST['ctf']['ID'] : null); - } - - - - public function AddLink() { - return Controller::join_links($this->Link(), 'add'); - } - - /** - * @return FieldList - */ - public function createFieldList() { - $fieldset = new FieldList(); - foreach($this->fieldTypes as $key => $fieldType){ - $fieldset->push(new $fieldType($key)); - } - return $fieldset; - } - - public function setController($controller) { - $this->controller = $controller; - return $this; - } - - public function setTemplatePopup($template) { - $this->templatePopup = $template; - return $this; - } - - //////////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Return the object-specific fields for the given record, to be shown in the detail pop-up - * - * This won't include all the CTF-specific 'plumbing; this method is called by self::getFieldsFor() - * and the result is then processed further to get the actual FieldList for the form. - * - * The default implementation of this processes the value of $this->detailFormFields; consequently, if you want to - * set the value of the fields to something that $this->detailFormFields doesn't allow, you can do so by overloading - * this method. - */ - public function getCustomFieldsFor($childData) { - if($this->detailFormFields instanceof FieldList) { - return $this->detailFormFields; - } - - $fieldsMethod = $this->detailFormFields; - - if(!is_string($fieldsMethod)) { - $this->detailFormFields = 'getCMSFields'; - $fieldsMethod = 'getCMSFields'; - } - - if(!$childData->hasMethod($fieldsMethod)) { - $fieldsMethod = 'getCMSFields'; - } - - return $childData->$fieldsMethod(); - } - - public function getFieldsFor($childData) { - $detailFields = $this->getCustomFieldsFor($childData); - - // the ID field confuses the Controller-logic in finding the right view for ReferencedField - $detailFields->removeByName('ID'); - - // only add childID if we're not adding a record - if($childData->ID) { - $detailFields->push(new HiddenField('ctf[childID]', '', $childData->ID)); - } - - /* TODO: Figure out how to implement this - if($this->getParentClass()) { - $detailFields->push(new HiddenField('ctf[parentClass]', '', $this->getParentClass())); - - // Hack for model admin: model admin will have included a dropdown for the relation itself - $parentIdName = $this->getParentIdName($this->getParentClass(), $this->sourceClass()); - if($parentIdName) { - $detailFields->removeByName($parentIdName); - $detailFields->push(new HiddenField($parentIdName, '', $this->sourceID())); - } - } - */ - - return $detailFields; - } - - public function getValidatorFor($childData) { - // if no custom validator is set, and there's on present on the object (e.g. Member), use it - if(!isset($this->detailFormValidator) && $childData->hasMethod('getValidator')) { - $this->detailFormValidator = $childData->getValidator(); - } - return $this->detailFormValidator; - } - - //////////////////////////////////////////////////////////////////////////////////////////////////// - - public function add() { - if(!$this->can('add')) return; - - return $this->customise(array( - 'DetailForm' => $this->AddForm(), - ))->renderWith($this->templatePopup); - } - - public function AddForm($childID = null) { - $className = $this->sourceClass(); - $childData = new $className(); - - $fields = $this->getFieldsFor($childData); - $validator = $this->getValidatorFor($childData); - - $form = new $this->popupClass( - $this, - 'AddForm', - $fields, - $validator, - false, - $childData - ); - - $form->loadDataFrom($childData); - - return $form; - } - - /** - * @deprecated 3.1 - */ - public function setRelationAutoSetting($value) { - Deprecation::notice('3.0', 'Manipulate the DataList instead.'); - return $this; - } - - /** - * Use the URL-Parameter "action_saveComplexTableField" - * to provide a clue to the main controller if the main form has to be rendered, - * even if there is no action relevant for the main controller (to provide the instance of ComplexTableField - * which in turn saves the record. - * - * This is for adding new item records. {@link ComplexTableField_ItemRequest::saveComplexTableField()} - * - * @see Form::ReferencedField - */ - public function saveComplexTableField($data, $form, $params) { - $className = $this->sourceClass(); - $childData = new $className(); - $form->saveInto($childData); - - try { - $childData->write(); - } catch(ValidationException $e) { - $form->sessionMessage($e->getResult()->message(), 'bad'); - return Controller::curr()->redirectBack(); - } - - // Save this item into the given relationship - $this->getDataList()->add($childData); - - $referrer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : null; - - $closeLink = sprintf( - '(%s)', - $referrer, - _t('ComplexTableField.CLOSEPOPUP', 'Close Popup') - ); - - $editLink = Controller::join_links($this->Link(), 'item/' . $childData->ID . '/edit'); - - $message = _t( - 'ComplexTableField.SUCCESSADD2', 'Added {name}', - array('name' => $childData->singular_name()) - ); - $message .= '' . $childData->Title . '' . $closeLink; - - $form->sessionMessage($message, 'good'); - - return Controller::curr()->redirectBack(); - } -} - -/** - * @todo Tie this into ComplexTableField_Item better. - * @package forms - * @subpackage fields-relational - */ -class ComplexTableField_ItemRequest extends TableListField_ItemRequest { - protected $ctf; - protected $itemID; - protected $methodName; - - static $url_handlers = array( - '$Action!' => '$Action', - '' => 'index', - ); - - public function Link($action = null) { - return Controller::join_links($this->ctf->Link(), '/item/', $this->itemID, $action); - } - - public function index() { - return $this->show(); - } - - /** - * Just a hook, processed in {DetailForm()} - * - * @return String - */ - public function show() { - if($this->ctf->Can('show') !== true) { - return false; - } - - $this->methodName = "show"; - return $this->renderWith($this->ctf->templatePopup); - } - - /** - * Returns a 1-element data object set that can be used for pagination. - */ - /* this doesn't actually work :-( - public function Paginator() { - $paginatingSet = new ArrayList(array($this->dataObj())); - $start = isset($_REQUEST['ctf']['start']) ? $_REQUEST['ctf']['start'] : 0; - $paginatingSet->setPageLimits($start, 1, $this->ctf->TotalCount()); - return $paginatingSet; - } - */ - - /** - * Just a hook, processed in {DetailForm()} - * - * @return String - */ - public function edit() { - if($this->ctf->Can('edit') !== true) { - return false; - } - - $this->methodName = "edit"; - - return $this->renderWith($this->ctf->templatePopup); - } - - public function delete($request) { - // Protect against CSRF on destructive action - $token = $this->ctf->getForm()->getSecurityToken(); - if(!$token->checkRequest($request)) return $this->httpError(400); - - if($this->ctf->Can('delete') !== true) { - return false; - } - - $this->ctf->getDataList()->removeByID($this->itemID); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Return the data object being manipulated - */ - public function dataObj() { - // used to discover fields if requested and for population of field - if(is_numeric($this->itemID)) { - // we have to use the basedataclass, otherwise we might exclude other subclasses - return DataObject::get_by_id( - ClassInfo::baseDataClass(Object::getCustomClass($this->ctf->sourceClass())), $this->itemID); - } - - } - - /** - * Renders view, edit and add, depending on the given information. - * The form needs several parameters to function independently of its "parent-form", some derived from the context - * into a hidden-field, some derived from the parent context (which is not accessible here) and delivered by - * GET:ID, Identifier of the currently edited record (only if record is loaded). - * , Link back to the correct parent record (e.g. "parentID"). - * parentClass, Link back to correct container-class (the parent-record might have many 'has-one'-relationships) - * CAUTION: "ID" in the DetailForm would be the "childID" in the overview table. - * - * @param int $childID - */ - public function DetailForm($childID = null) { - $childData = $this->dataObj(); - - $fields = $this->ctf->getFieldsFor($childData); - $validator = $this->ctf->getValidatorFor($childData); - $readonly = ($this->methodName == "show"); - - $form = new $this->ctf->popupClass( - $this, - "DetailForm", - $fields, - $validator, - $readonly, - $childData - ); - // Don't use ComplexTableField_Popup.ss - $form->setTemplate('Form'); - - $form->loadDataFrom($childData); - if ($readonly) $form->makeReadonly(); - - return $form; - } - - /** - * Use the URL-Parameter "action_saveComplexTableField" - * to provide a clue to the main controller if the main form has to be rendered, - * even if there is no action relevant for the main controller (to provide the instance of ComplexTableField - * which in turn saves the record. - * - * This is for editing existing item records. {@link ComplexTableField::saveComplexTableField()} - * - * @see Form::ReferencedField - */ - public function saveComplexTableField($data, $form, $request) { - $dataObject = $this->dataObj(); - - try { - $form->saveInto($dataObject); - $dataObject->write(); - } catch(ValidationException $e) { - $form->sessionMessage($e->getResult()->message(), 'bad'); - return Controller::curr()->redirectBack(); - } - - // Save this item into the given relationship - $this->ctf->getDataList()->add($dataObject); - - $referrer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : null; - - $closeLink = sprintf( - '(%s)', - $referrer, - _t('ComplexTableField.CLOSEPOPUP', 'Close Popup') - ); - $message = sprintf( - _t('ComplexTableField.SUCCESSEDIT', 'Saved %s %s %s'), - $dataObject->singular_name(), - '"' . htmlspecialchars($dataObject->Title, ENT_QUOTES) . '"', - $closeLink - ); - - $form->sessionMessage($message, 'good'); - - return Controller::curr()->redirectBack(); - } - - public function PopupCurrentItem() { - return $_REQUEST['ctf']['start']+1; - } - - public function PopupFirstLink() { - $this->ctf->LinkToItem(); - - if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start']) - || $_REQUEST['ctf']['start'] == 0) { - return null; - } - - $start = 0; - return Controller::join_links($this->Link(), "$this->methodName?ctf[start]={$start}"); - } - - public function PopupLastLink() { - if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start']) - || $_REQUEST['ctf']['start'] == $this->TotalCount()-1) { - return null; - } - - $start = $this->TotalCount - 1; - return Controller::join_links($this->Link(), "$this->methodName?ctf[start]={$start}"); - } - - public function PopupNextLink() { - if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start']) - || $_REQUEST['ctf']['start'] == $this->TotalCount()-1) { - return null; - } - - $start = $_REQUEST['ctf']['start'] + 1; - return Controller::join_links($this->Link(), "$this->methodName?ctf[start]={$start}"); - } - - public function PopupPrevLink() { - if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start']) - || $_REQUEST['ctf']['start'] == 0) { - return null; - } - - $start = $_REQUEST['ctf']['start'] - 1; - return Controller::join_links($this->Link(), "$this->methodName?ctf[start]={$start}"); - } - - /** - * Method handles pagination in asset popup. - * - * @return Object SS_List - */ - - public function Pagination() { - $this->pageSize = 9; - $currentItem = $this->PopupCurrentItem(); - $result = new ArrayList(); - if($currentItem < 6) { - $offset = 1; - } elseif($this->TotalCount() - $currentItem <= 4) { - $offset = $currentItem - (10 - ($this->TotalCount() - $currentItem)); - $offset = $offset <= 0 ? 1 : $offset; - } else { - $offset = $currentItem - 5; - } - for($i = $offset;$i <= $offset + $this->pageSize && $i <= $this->TotalCount();$i++) { - $start = $i - 1; - $links['link'] = Controller::join_links($this->Link() . "$this->methodName?ctf[start]={$start}"); - $links['number'] = $i; - $links['active'] = $i == $currentItem ? false : true; - $result->push(new ArrayData($links)); - } - return $result; - } - - public function ShowPagination() { - return false; - } - - - /** - * ################################# - * Utility - * ################################# - */ - - /** - * Manually overwrites the parent-ID relations. - * @see setParentClass() - * - * @param String $str Example: FamilyID (when one Individual has_one Family) - */ - public function setParentIdName($str) { - throw new Exception("setParentIdName is no longer necessary"); - } - - public function setTemplatePopup($template) { - $this->templatePopup = $template; - } - - -} - -/** - * Single row of a {@link ComplexTableField}. - * @package forms - * @subpackage fields-relational - */ -class ComplexTableField_Item extends TableListField_Item { - public function Link($action = null) { - return Controller::join_links($this->parent->Link(), '/item/', $this->item->ID, $action); - } - - public function EditLink() { - return Controller::join_links($this->Link(), "edit"); - } - - public function ShowLink() { - return Controller::join_links($this->Link(), "show"); - } - - public function DeleteLink() { - return Controller::join_links($this->Link(), "delete"); - } - - /** - * @param String $action - * @return boolean - */ - public function IsDefaultAction($action) { - return ($action == $this->parent->defaultAction); - } -} - - -/** - * ComplexTablefield_popup is rendered with a lightbox and can load a more - * detailed view of the source class your presenting. - * You can customise the fields and requirements as well as any - * permissions you might need. - * @package forms - * @subpackage fields-relational - */ -class ComplexTableField_Popup extends Form { - protected $sourceClass; - - protected $dataObject; - - public function __construct($controller, $name, $fields, $validator, $readonly, $dataObject) { - $this->dataObject = $dataObject; - - - $actions = new FieldList(); - if(!$readonly) { - $actions->push( - FormAction::create( - "saveComplexTableField", - _t('CMSMain.SAVE', 'Save') - ) - ->addExtraClass('save ss-ui-action-constructive') - ->setUseButtonTag(true) - ->setAttribute('data-icon', 'accept') - ); - } - - parent::__construct($controller, $name, $fields, $actions, $validator); - - if(!$this->dataObject->canEdit()) $this->makeReadonly(); - } - - public function forTemplate() { - $ret = parent::forTemplate(); - - Requirements::css(FRAMEWORK_DIR . '/css/ComplexTableField_popup.css'); - Requirements::javascript(FRAMEWORK_DIR . "/thirdparty/prototype/prototype.js"); - Requirements::javascript(FRAMEWORK_DIR . "/thirdparty/behaviour/behaviour.js"); - Requirements::javascript(FRAMEWORK_DIR . "/thirdparty/scriptaculous/scriptaculous.js"); - Requirements::javascript(FRAMEWORK_DIR . "/thirdparty/scriptaculous/scriptaculous/controls.js"); - Requirements::add_i18n_javascript(FRAMEWORK_DIR . '/javascript/lang'); - Requirements::javascript(FRAMEWORK_DIR . "/javascript/ComplexTableField_popup.js"); - - // Append requirements from instance callbacks - $parent = $this->getParentController(); - if($parent instanceof ComplexTableField) { - $callback = $parent->requirementsForPopupCallback; - } else { - $callback = $parent->getParentController()->requirementsForPopupCallback; - } - if($callback) call_user_func($callback, $this); - - return $ret; - } - - public function getTemplate() { - return 'Form'; - } - - /** - * @return ComplexTableField_ItemRequest - */ - public function getParentController() { - return $this->controller; - } -} - - diff --git a/forms/HasManyComplexTableField.php b/forms/HasManyComplexTableField.php deleted file mode 100644 index 667c44bda..000000000 --- a/forms/HasManyComplexTableField.php +++ /dev/null @@ -1,181 +0,0 @@ -Usage - * - * - * $tablefield = new HasManyComplexTableField( - * $this, - * 'MyFruits', - * 'Fruit', - * array( - * 'Name' => 'Name', - * 'Color' => 'Color' - * ), - * 'getCMSFields_forPopup' - * ); - * - * - * Notice: You still have different ways to customize the popup window as in the parent-class - * {@link ComplexTableField}. - * - * @see http://doc.silverstripe.org/tutorial/5-dataobject-relationship-management - * - * @deprecated 3.1 Use GridField with GridFieldConfig_RelationEditor - * - * @package forms - * @subpackage fields-relational - */ -class HasManyComplexTableField extends ComplexTableField { - - public $joinField; - - protected $addTitle; - - // If you change the value, do not forget to change it also in the JS file - protected $htmlListEndName = 'CheckedList'; - - // If you change the value, do not forget to change it also in the JS file - protected $htmlListField = 'selected'; - - public $template = 'RelationComplexTableField'; - - public $itemClass = 'HasManyComplexTableField_Item'; - - protected $relationAutoSetting = false; - - public function __construct($controller, $name, $sourceClass, $fieldList = null, $detailFormFields = null, - $sourceFilter = "", $sourceSort = "", $sourceJoin = "") { - - parent::__construct($controller, $name, $sourceClass, $fieldList, $detailFormFields, - $sourceFilter, $sourceSort, $sourceJoin); - - Deprecation::notice('3.0', 'Use GridField with GridFieldConfig_RelationEditor', Deprecation::SCOPE_CLASS); - - $this->Markable = true; - - if($controllerClass = $this->controllerClass()) { - $this->joinField = $this->getParentIdName($controllerClass, $this->sourceClass); - if(!$this->joinField) { - user_error("Can't find a has_one relationship from '$this->sourceClass' to '$controllerClass'", - E_USER_WARNING); - } - } else { - user_error("Can't figure out the data class of $controller", E_USER_WARNING); - } - - } - - public function FieldHolder($properties = array()) { - $ret = parent::FieldHolder($properties); - - Requirements::add_i18n_javascript(FRAMEWORK_DIR . '/javascript/lang'); - Requirements::javascript(FRAMEWORK_DIR . "/javascript/HasManyFileField.js"); - Requirements::javascript(FRAMEWORK_DIR . '/javascript/RelationComplexTableField.js'); - Requirements::css(FRAMEWORK_DIR . '/css/HasManyFileField.css'); - - return $ret; - } - - /** - * Try to determine the DataObject that this field is built on top of - */ - public function controllerClass() { - if($this->controller instanceof DataObject) return $this->controller->class; - elseif($this->controller instanceof Controller) return $this->controller->data()->class; - } - - public function getControllerID() { - return $this->controller->ID; - } - - public function saveInto(DataObjectInterface $record) { - $fieldName = $this->name; - $saveDest = $record->$fieldName(); - - if(!$saveDest) { - user_error("HasManyComplexTableField::saveInto() Field $fieldName not found on $record->class.$record->ID" - , E_USER_ERROR); - } - - $items = array(); - - if($list = $this->value[ $this->htmlListField ]) { - if($list != 'undefined') - $items = explode(',', $list); - } - - $saveDest->setByIDList($items); - } - - public function setAddTitle($addTitle) { - if(is_string($addTitle)) - $this->addTitle = $addTitle; - } - - public function Title() { - return $this->addTitle ? $this->addTitle : parent::Title(); - } - - /** - * Get the IDs of the selected items, in a has_many or many_many relation - */ - public function selectedItemIDs() { - $fieldName = $this->name; - $selectedItems = $this->form->getRecord()->$fieldName(); - $itemIDs = array(); - foreach($selectedItems as $item) $itemIDs[] = $item->ID; - return $itemIDs; - } - - public function ExtraData() { - $items = array(); - - $list = implode(',', $this->selectedItemIDs()); - $inputId = $this->id() . '_' . $this->htmlListEndName; - return << -HTML; - } -} - -/** - * Single record of a {@link HasManyComplexTableField} field. - * @package forms - * @subpackage fields-relational - */ -class HasManyComplexTableField_Item extends ComplexTableField_Item { - - public function MarkingCheckbox() { - $name = $this->parent->getName() . '[]'; - - if(!$this->parent->joinField) { - user_error("joinField not set in HasManyComplexTableField '{$this->parent->name}'", E_USER_WARNING); - return null; - } - - $joinVal = $this->item->{$this->parent->joinField}; - $parentID = $this->parent->getControllerID(); - - if($this->parent->IsReadOnly || ($joinVal > 0 && $joinVal != $parentID)) - return "item->ID}\" - disabled=\"disabled\"/>"; - else if($joinVal == $parentID) - return "item->ID}\" - checked=\"checked\"/>"; - else - return "item->ID}\"/>"; - } -} - - diff --git a/forms/HasOneComplexTableField.php b/forms/HasOneComplexTableField.php deleted file mode 100644 index b424ec34e..000000000 --- a/forms/HasOneComplexTableField.php +++ /dev/null @@ -1,109 +0,0 @@ -Usage - * - * - * $tablefield = new HasOneComplexTableField( - * $this, - * 'MyOnlyFruit', - * 'Fruit', - * array( - * 'Name' => 'Name', - * 'Color' => 'Color' - * ), - * 'getCMSFields_forPopup' - * ); - * - * - * **Notice** : You still have different ways to customize the popup window as in the parent-class - * [ComplexTableField](ComplexTableField). - * - * This field is made to manage a **has_one** relation. In the SilverStripe relation between DataObjects, you can use - * this relation for **1-to-1** and **1-to-many** relations. - * By default, a HasOneComplexTableField manages a **1-to-many** relation. If you want to specify that the relation - * that you manage is a **1-to-1** relation, add this code : - * - * - * $tablefield->setOneToOne(); - * - * - * @package forms - * @subpackage fields-relational - */ -class HasOneComplexTableField extends HasManyComplexTableField { - - public $itemClass = 'HasOneComplexTableField_Item'; - - public $isOneToOne = false; - - public function getParentIdName($parentClass, $childClass) { - return $this->getParentIdNameRelation($parentClass, $childClass, 'has_one'); - } - - public function getControllerJoinID() { - return $this->controller->{$this->joinField}; - } - - public function saveInto(DataObjectInterface $record) { - $fieldName = $this->name; - $fieldNameID = $fieldName . 'ID'; - - $record->$fieldNameID = 0; - if($val = $this->value[ $this->htmlListField ]) { - if($val != 'undefined') - $record->$fieldNameID = $val; - } - - $record->write(); - } - - public function setOneToOne() { - $this->isOneToOne = true; - } - - public function isChildSet($childID) { - return DataObject::get($this->controllerClass(), '"' . $this->joinField . "\" = '$childID'"); - } - - public function ExtraData() { - $val = $this->getControllerJoinID() ? $this->getControllerJoinID() : ''; - $inputId = $this->id() . '_' . $this->htmlListEndName; - return << -HTML; - } -} - -/** - * Single record of a {@link HasOneComplexTableField} field. - * @package forms - * @subpackage fields-relational - */ -class HasOneComplexTableField_Item extends ComplexTableField_Item { - - public function MarkingCheckbox() { - $name = $this->parent->getName() . '[]'; - - $isOneToOne = $this->parent->isOneToOne; - $joinVal = $this->parent->getControllerJoinID(); - $childID = $this->item->ID; - - if($this->parent->IsReadOnly || ($isOneToOne && $joinVal != $childID && $this->parent->isChildSet($childID))) - return "item->ID}\" - disabled=\"disabled\"/>"; - else if($joinVal == $childID) - return "item->ID}\" - checked=\"checked\"/>"; - else - return "item->ID}\"/>"; - } -} - diff --git a/forms/ManyManyComplexTableField.php b/forms/ManyManyComplexTableField.php deleted file mode 100644 index c0fb9d064..000000000 --- a/forms/ManyManyComplexTableField.php +++ /dev/null @@ -1,116 +0,0 @@ -Usage - * - * - * $tablefield = new ManyManyComplexTableField( - * $this, - * 'MyFruits', - * 'Fruit', - * array( - * 'Name' => 'Name', - * 'Color' => 'Color' - * ), - * 'getCMSFields_forPopup' - * ); - * - * - * @deprecated 3.1 Use GridField with GridFieldConfig_RelationEditor - * - * @package forms - * @subpackage fields-relational - */ -class ManyManyComplexTableField extends HasManyComplexTableField { - - private $manyManyParentClass; - - public $itemClass = 'ManyManyComplexTableField_Item'; - - public function __construct($controller, $name, $sourceClass, $fieldList = null, $detailFormFields = null, - $sourceFilter = "", $sourceSort = "", $sourceJoin = "") { - - Deprecation::notice('3.0', 'Use GridField with GridFieldConfig_RelationEditor', Deprecation::SCOPE_CLASS); - - parent::__construct($controller, $name, $sourceClass, $fieldList, $detailFormFields, - $sourceFilter, $sourceSort, $sourceJoin); - - $classes = array_reverse(ClassInfo::ancestry($this->controllerClass())); - foreach($classes as $class) { - $singleton = singleton($class); - $manyManyRelations = $singleton->uninherited('many_many', true); - if(isset($manyManyRelations) && array_key_exists($this->name, $manyManyRelations)) { - $this->manyManyParentClass = $class; - $manyManyTable = $class . '_' . $this->name; - break; - } - $belongsManyManyRelations = $singleton->uninherited( 'belongs_many_many', true ); - if( isset( $belongsManyManyRelations ) && array_key_exists( $this->name, $belongsManyManyRelations ) ) { - $this->manyManyParentClass = $class; - $manyManyTable = $belongsManyManyRelations[$this->name] . '_' . $this->name; - break; - } - } - $tableClasses = ClassInfo::dataClassesFor($this->sourceClass); - $source = array_shift($tableClasses); - $sourceField = $this->sourceClass; - if($this->manyManyParentClass == $sourceField) - $sourceField = 'Child'; - $parentID = $this->controller->ID; - - $this->sourceJoin .= " LEFT JOIN \"$manyManyTable\" - ON (\"$source\".\"ID\" = \"$manyManyTable\".\"{$sourceField}ID\" - AND \"{$this->manyManyParentClass}ID\" = '$parentID')"; - - $this->joinField = 'Checked'; - } - - public function getQuery() { - $query = parent::getQuery(); - $query->selectField("CASE WHEN \"{$this->manyManyParentClass}ID\" IS NULL THEN '0' ELSE '1' END", "Checked"); - $query->groupby[] = "\"{$this->manyManyParentClass}ID\""; // necessary for Postgres - - return $query; - } - - public function getParentIdName($parentClass, $childClass) { - return $this->getParentIdNameRelation($parentClass, $childClass, 'many_many'); - } -} - -/** - * One record in a {@link ManyManyComplexTableField}. - * @package forms - * @subpackage fields-relational - */ -class ManyManyComplexTableField_Item extends ComplexTableField_Item { - - public function MarkingCheckbox() { - $name = $this->parent->getName() . '[]'; - - if($this->parent->IsReadOnly) - return "item->ID}\" - disabled=\"disabled\"/>"; - else if($this->item->{$this->parent->joinField}) - return "item->ID}\" - checked=\"checked\"/>"; - else - return "item->ID}\"/>"; - } -} - - diff --git a/forms/TableField.php b/forms/TableField.php deleted file mode 100644 index c017bed36..000000000 --- a/forms/TableField.php +++ /dev/null @@ -1,724 +0,0 @@ -filterField and $this->filterValue. This has the - * advantage of auto-populating new records - */ - protected $filterField = null; - - /** - * A "Field = Value" filter can be specified by setting $this->filterField and $this->filterValue. This has the - * advantage of auto-populating new records - */ - protected $filterValue = null; - - /** - * @var $fieldTypes FieldList - * Caution: Use {@setExtraData()} instead of manually adding HiddenFields if you want to - * preset relations or other default data. - */ - protected $fieldTypes; - - /** - * @var $template string Template-Overrides - */ - protected $template = "TableField"; - - /** - * @var $extraData array Any extra data that need to be included, e.g. to retain - * has-many relations. Format: array('FieldName' => 'Value') - */ - protected $extraData; - - protected $tempForm; - - /** - * Influence output without having to subclass the template. - */ - protected $permissions = array( - "edit", - "delete", - "add", - //"export", - ); - - public $transformationConditions = array(); - - /** - * @var $requiredFields array Required fields as a numerical array. - * Please use an instance of Validator on the including - * form. - */ - protected $requiredFields = null; - - /** - * Shows a row of empty fields for adding a new record - * (turned on by default). - * Please use {@link TableField::$permissions} to control - * if the "add"-functionality incl. button is shown at all. - * - * @param boolean $showAddRow - */ - public $showAddRow = true; - - /** - * @param $name string The fieldname - * @param $sourceClass string The source class of this field - * @param $fieldList array An array of field headings of Fieldname => Heading Text (eg. heading1) - * @param $fieldTypes array An array of field types of fieldname => fieldType (eg. formfield). Do not use for - * extra data/hiddenfields. - * @param $filterField string The field to filter by. Give the filter value in $sourceFilter. The value will - * automatically be set on new records. - * @param $sourceFilter string If $filterField has a value, then this is the value to filter by. Otherwise, it is - * a SQL filter expression. - * @param $editExisting boolean (Note: Has to stay on this position for legacy reasons) - * @param $sourceSort string - * @param $sourceJoin string - */ - public function __construct($name, $sourceClass, $fieldList = null, $fieldTypes, $filterField = null, - $sourceFilter = null, $editExisting = true, $sourceSort = null, $sourceJoin = null) { - - $this->fieldTypes = $fieldTypes; - $this->filterField = $filterField; - - $this->editExisting = $editExisting; - - // If we specify filterField, then an implicit source filter of "filterField = sourceFilter" is used. - if($filterField) { - $this->filterValue = $sourceFilter; - $sourceFilter = "\"$filterField\" = '" . Convert::raw2sql($sourceFilter) . "'"; - } - parent::__construct($name, $sourceClass, $fieldList, $sourceFilter, $sourceSort, $sourceJoin); - } - - /** - * Displays the headings on the template - * - * @return SS_List - */ - public function Headings() { - $i=0; - foreach($this->fieldList as $fieldName => $fieldTitle) { - $extraClass = "col".$i; - $class = $this->fieldTypes[$fieldName]; - if(is_object($class)) $class = ""; - $class = $class." ".$extraClass; - $headings[] = new ArrayData(array("Name" => $fieldName, "Title" => $fieldTitle, "Class" => $class)); - $i++; - } - return new ArrayList($headings); - } - - /** - * Calculates the number of columns needed for colspans - * used in template - * - * @return int - */ - public function ItemCount() { - return count($this->fieldList); - } - - /** - * Displays the items from {@link sourceItems()} using the encapsulation object. - * If the field value has been set as an array (e.g. after a failed validation), - * it generates the rows from array data instead. - * Used in the formfield template to iterate over each row. - * - * @return SS_List Collection of {@link TableField_Item} - */ - public function Items() { - // holds TableField_Item instances - $items = new ArrayList(); - - $sourceItems = $this->sourceItems(); - - // either load all rows from the field value, - // (e.g. when validation failed), or from sourceItems() - if($this->value) { - if(!$sourceItems) $sourceItems = new ArrayList(); - - // get an array keyed by rows, rather than values - $rows = $this->sortData(ArrayLib::invert($this->value)); - // ignore all rows which are already saved - if(isset($rows['new'])) { - if($sourceItems instanceof DataList) { - $sourceItems = new ArrayList($sourceItems->toArray()); - } - - $newRows = $this->sortData($rows['new']); - // iterate over each value (not each row) - $i = 0; - foreach($newRows as $idx => $newRow){ - // set a pseudo-ID - $newRow['ID'] = "new"; - - // unset any extradata - foreach($newRow as $k => $v){ - if($this->extraData && array_key_exists($k, $this->extraData)){ - unset($newRow[$k]); - } - } - - // generate a temporary DataObject container (not saved in the database) - $sourceClass = $this->sourceClass(); - $sourceItems->add(new $sourceClass($newRow)); - - $i++; - } - } - } - - // generate a new TableField_Item instance from each collected item - if($sourceItems) foreach($sourceItems as $sourceItem) { - $items->push($this->generateTableFieldItem($sourceItem)); - } - - // add an empty TableField_Item for a single "add row" - if($this->showAddRow && $this->Can('add')) { - $items->push(new TableField_Item(null, $this, null, $this->fieldTypes, true)); - } - - return $items; - } - - /** - * Generates a new {@link TableField} instance - * by loading a FieldList for this row into a temporary form. - * - * @param DataObject $dataObj - * @return TableField_Item - */ - protected function generateTableFieldItem($dataObj) { - // Load the data in to a temporary form (for correct field types) - $form = new Form( - $this, - null, - $this->FieldSetForRow(), - new FieldList() - ); - $form->loadDataFrom($dataObj); - - // Add the item to our new ArrayList, with a wrapper class. - return new TableField_Item($dataObj, $this, $form, $this->fieldTypes); - } - - /** - * @return array - */ - public function FieldList() { - return $this->fieldList; - } - - /** - * Saves the Dataobjects contained in the field - */ - public function saveInto(DataObjectInterface $record) { - // CMS sometimes tries to set the value to one. - if(is_array($this->value)){ - $newFields = array(); - - // Sort into proper array - $value = ArrayLib::invert($this->value); - $dataObjects = $this->sortData($value, $record->ID); - - // New fields are nested in their own sub-array, and need to be sorted separately - if(isset($dataObjects['new']) && $dataObjects['new']) { - $newFields = $this->sortData($dataObjects['new'], $record->ID); - } - - // Update existing fields - // @todo Should this be in an else{} statement? - $savedObjIds = $this->saveData($dataObjects, $this->editExisting); - - // Save newly added record - if($savedObjIds || $newFields) { - $savedObjIds = $this->saveData($newFields,false); - } - - // Add the new records to the DataList - if($savedObjIds) foreach($savedObjIds as $id => $status) { - $this->getDataList()->add($id); - } - - // Update the internal source items cache - $this->value = null; - $items = $this->sourceItems(); - - // FormResponse::update_dom_id($this->id(), $this->FieldHolder()); - } - } - - /** - * Get all {@link FormField} instances necessary for a single row, - * respecting the casting set in {@link $fieldTypes}. - * Doesn't populate with any data. Optionally performs a readonly - * transformation if {@link $IsReadonly} is set, or the current user - * doesn't have edit permissions. - * - * @return FieldList - */ - public function FieldSetForRow() { - $fieldset = new FieldList(); - if($this->fieldTypes){ - foreach($this->fieldTypes as $key => $fieldType) { - if(isset($fieldType->class) && is_subclass_of($fieldType, 'FormField')) { - // using clone, otherwise we would just add stuff to the same field-instance - $field = clone $fieldType; - } elseif(strpos($fieldType, '(') === false) { - $field = new $fieldType($key); - } else { - $fieldName = $key; - $fieldTitle = ""; - $field = eval("return new $fieldType;"); - } - if($this->IsReadOnly || !$this->Can('edit')) { - $field = $field->performReadonlyTransformation(); - } - $fieldset->push($field); - } - }else{ - USER_ERROR("TableField::FieldSetForRow() - Fieldtypes were not specified",E_USER_WARNING); - } - - return $fieldset; - } - - public function performReadonlyTransformation() { - $clone = clone $this; - $clone->permissions = array('show'); - $clone->setReadonly(true); - return $clone; - } - - public function performDisabledTransformation() { - $clone = clone $this; - $clone->setPermissions(array('show')); - $clone->setDisabled(true); - return $clone; - } - - /** - * Needed for Form->callfieldmethod. - */ - public function getField($fieldName, $combinedFieldName = null) { - $fieldSet = $this->FieldSetForRow(); - $field = $fieldSet->dataFieldByName($fieldName); - if(!$field) { - return false; - } - - if($combinedFieldName) { - $field->Name = $combinedFieldName; - } - - return $field; - } - - /** - * Called on save, it creates the appropriate objects and writes them - * to the database. - * - * @param SS_List $dataObjects - * @param boolean $existingValues If set to TRUE, it tries to find existing objects - * based on the database IDs passed as array keys in $dataObjects parameter. - * If set to FALSE, it will always create new object (default: TRUE) - * @return array Array of saved object IDs in the key, and the status ("Updated") in the value - */ - public function saveData($dataObjects, $existingValues = true) { - if(!$dataObjects) return false; - - $savedObjIds = array(); - $fieldset = $this->FieldSetForRow(); - - // add hiddenfields - if($this->extraData) { - foreach($this->extraData as $fieldName => $fieldValue) { - $fieldset->push(new HiddenField($fieldName)); - } - } - - $form = new Form($this, null, $fieldset, new FieldList()); - - foreach ($dataObjects as $objectid => $fieldValues) { - // 'new' counts as an empty column, don't save it - if($objectid === "new") continue; - - // extra data was creating fields, but - if($this->extraData) { - $fieldValues = array_merge( $this->extraData, $fieldValues ); - } - - // either look for an existing object, or create a new one - if($existingValues) { - $obj = DataObject::get_by_id($this->sourceClass(), $objectid); - } else { - $sourceClass = $this->sourceClass(); - $obj = new $sourceClass(); - } - - // Legacy: Use the filter as a predefined relationship-ID - if($this->filterField && $this->filterValue) { - $filterField = $this->filterField; - $obj->$filterField = $this->filterValue; - } - - // Determine if there is changed data for saving - $dataFields = array(); - foreach($fieldValues as $type => $value) { - // if the field is an actual datafield (not a preset hiddenfield) - if(is_array($this->extraData)) { - if(!in_array($type, array_keys($this->extraData))) { - $dataFields[$type] = $value; - } - // all fields are real - } else { - $dataFields[$type] = $value; - } - } - $dataValues = ArrayLib::array_values_recursive($dataFields); - // determine if any of the fields have a value (loose checking with empty()) - $hasData = false; - foreach($dataValues as $value) { - if(!empty($value)) $hasData = true; - } - - if($hasData) { - $form->loadDataFrom($fieldValues, true); - $form->saveInto($obj); - - $objectid = $obj->write(); - - $savedObjIds[$objectid] = "Updated"; - } - - } - - return $savedObjIds; - } - - /** - * Organises the data in the appropriate manner for saving - * - * @param array $data - * @param int $recordID - * @return array Collection of maps suitable to construct DataObjects - */ - public function sortData($data, $recordID = null) { - if(!$data) return false; - - $sortedData = array(); - - foreach($data as $field => $rowData) { - $i = 0; - if(!is_array($rowData)) continue; - - foreach($rowData as $id => $value) { - if($value == '$recordID') $value = $recordID; - - if($value) $sortedData[$id][$field] = $value; - - $i++; - } - - // TODO ADD stuff for removing rows with incomplete data - } - - return $sortedData; - } - - /** - * @param $extraData array - */ - public function setExtraData($extraData) { - $this->extraData = $extraData; - return $this; - } - - /** - * @return array - */ - public function getExtraData() { - return $this->extraData; - } - - /** - * Sets the template to be rendered with - */ - public function FieldHolder($properties = array()) { - Requirements::javascript(FRAMEWORK_DIR . '/thirdparty/jquery/jquery.js'); - Requirements::javascript(THIRDPARTY_DIR . "/prototype/prototype.js"); - Requirements::javascript(FRAMEWORK_DIR . '/thirdparty/behaviour/behaviour.js'); - Requirements::add_i18n_javascript(FRAMEWORK_DIR . '/javascript/lang'); - Requirements::javascript(FRAMEWORK_DIR . '/javascript/TableListField.js'); - Requirements::javascript(FRAMEWORK_DIR . '/javascript/TableField.js'); - Requirements::css(FRAMEWORK_DIR . '/css/TableListField.css'); - - $obj = $properties ? $this->customise($properties) : $this; - return $obj->renderWith($this->template); - } - - public function setTransformationConditions($conditions) { - $this->transformationConditions = $conditions; - return $this; - } - - public function php($data) { - $valid = true; - - if($data['methodName'] != 'delete') { - $items = $this->Items(); - if($items) foreach($items as $item) { - foreach($item->Fields() as $field) { - $valid = $field->validate($this) && $valid; - } - } - - return $valid; - } else { - return $valid; - } - } - - public function validate($validator) { - $errorMessage = ''; - $valid = true; - - // @todo should only contain new elements - $items = $this->Items(); - if($items) foreach($items as $item) { - foreach($item->Fields() as $field) { - $valid = $field->validate($validator) && $valid; - } - } - - //debug::show($this->form->Message()); - if($this->requiredFields&&$sourceItemsNew&&$sourceItemsNew->count()) { - foreach ($this->requiredFields as $field) { - foreach($sourceItemsNew as $item){ - $cellName = $this->getName().'['.$item->ID.']['.$field.']'; - $cName = $this->getName().'[new]['.$field.'][]'; - - if($fieldObj = $fields->dataFieldByName($cellName)) { - if(!trim($fieldObj->Value())){ - $title = $fieldObj->Title(); - $errorMessage .= sprintf( - _t('TableField.ISREQUIRED', "In %s '%s' is required"), - $this->name, - $title - ); - $errorMessage .= "
"; - } - } - } - } - } - - if($errorMessage){ - $messageType .= "validation"; - $message .= "
".$errorMessage; - - $validator->validationError($this->name, $message, $messageType); - } - - return $valid; - } - - public function setRequiredFields($fields) { - $this->requiredFields = $fields; - return $this; - } -} - -/** - * Single record in a TableField. - * @package forms - * @subpackage fields-relational - * @see TableField - */ -class TableField_Item extends TableListField_Item { - - /** - * @var FieldList $fields - */ - protected $fields; - - protected $data; - - protected $fieldTypes; - - protected $isAddRow; - - protected $extraData; - - /** - * Each row contains a dataobject with any number of attributes - * @param $ID int The ID of the record - * @param $form Form A Form object containing all of the fields for this item. The data should be loaded in - * @param $fieldTypes array An array of name => fieldtype for use when creating a new field - * @param $parent TableListField The parent table for quick reference of names, and id's for storing values. - */ - public function __construct($item = null, $parent, $form, $fieldTypes, $isAddRow = false) { - $this->data = $form; - $this->fieldTypes = $fieldTypes; - $this->isAddRow = $isAddRow; - $this->item = $item; - - parent::__construct(($this->item) ? $this->item : new DataObject(), $parent); - - $this->fields = $this->createFields(); - } - /** - * Represents each cell of the table with an attribute. - * - * @return FieldList - */ - public function createFields() { - // Existing record - if($this->item && $this->data) { - $form = $this->data; - $this->fieldset = $form->Fields(); - $this->fieldset->removeByName('SecurityID'); - if($this->fieldset) { - $i=0; - foreach($this->fieldset as $field) { - $origFieldName = $field->getName(); - - // set unique fieldname with id - $combinedFieldName = $this->parent->getName() . "[" . $this->ID() . "][" . $origFieldName . "]"; - // ensure to set field to nested array notation - // if its an unsaved row, or the "add row" which is present by default - if($this->isAddRow || $this->ID() == 'new') $combinedFieldName .= '[]'; - - // get value - if(strpos($origFieldName,'.') === false) { - $value = $field->dataValue(); - } else { - // this supports the syntax fieldName = Relation.RelatedField - $fieldNameParts = explode('.', $origFieldName) ; - $tmpItem = $this->item; - for($j=0;$j$relationMethod; - } else { - $tmpItem = $tmpItem->$relationMethod(); - } - } - } - - $field->Name = $combinedFieldName; - $field->setValue($field->dataValue()); - $field->addExtraClass('col'.$i); - $field->setForm($this->data); - - // transformation - if(isset($this->parent->transformationConditions[$origFieldName])) { - $transformation = $this->parent->transformationConditions[$origFieldName]['transformation']; - $rule = str_replace("\$","\$this->item->", - $this->parent->transformationConditions[$origFieldName]['rule']); - $ruleApplies = null; - eval('$ruleApplies = ('.$rule.');'); - if($ruleApplies) { - $field = $field->$transformation(); - } - } - - // formatting - $item = $this->item; - $value = $field->Value(); - if(array_key_exists($origFieldName, $this->parent->fieldFormatting)) { - $format = str_replace('$value', "__VAL__", $this->parent->fieldFormatting[$origFieldName]); - $format = preg_replace('/\$([A-Za-z0-9-_]+)/','$item->$1', $format); - $format = str_replace('__VAL__', '$value', $format); - eval('$value = "' . $format . '";'); - $field->dontEscape = true; - $field->setValue($value); - } - - $this->fields[] = $field; - $i++; - } - } - // New record - } else { - $list = $this->parent->FieldList(); - $i=0; - foreach($list as $fieldName => $fieldTitle) { - if(strpos($fieldName, ".")) { - $shortFieldName = substr($fieldName, strpos($fieldName, ".")+1, strlen($fieldName)); - } else { - $shortFieldName = $fieldName; - } - $combinedFieldName = $this->parent->getName() . "[new][" . $shortFieldName . "][]"; - $fieldType = $this->fieldTypes[$fieldName]; - if(isset($fieldType->class) && is_subclass_of($fieldType, 'FormField')) { - $field = clone $fieldType; // we can't use the same instance all over, as we change names - $field->Name = $combinedFieldName; - } elseif(strpos($fieldType, '(') === false) { - //echo ("
  • Type: ".$fieldType." fieldName: ". $filedName. " Title: ".$fieldTitle."
  • "); - $field = new $fieldType($combinedFieldName,$fieldTitle); - } else { - $field = eval("return new " . $fieldType . ";"); - } - $field->addExtraClass('col'.$i); - $this->fields[] = $field; - $i++; - } - } - return new FieldList($this->fields); - } - - public function Fields($xmlSafe = true) { - return $this->fields; - } - - public function ExtraData() { - $content = ""; - $id = ($this->item->ID) ? $this->item->ID : "new"; - if($this->parent->getExtraData()) { - foreach($this->parent->getExtraData() as $fieldName=>$fieldValue) { - $name = $this->parent->getName() . "[" . $id . "][" . $fieldName . "]"; - if($this->isAddRow) $name .= '[]'; - $field = new HiddenField($name, null, $fieldValue); - $content .= $field->FieldHolder() . "\n"; - } - } - - return $content; - } - - /** - * Get the flag isAddRow of this item, - * to indicate if the item is that blank last row in the table which is not in the database - * - * @return boolean - */ - public function IsAddRow(){ - return $this->isAddRow; - } - -} - diff --git a/forms/TableListField.php b/forms/TableListField.php deleted file mode 100644 index ffee6e762..000000000 --- a/forms/TableListField.php +++ /dev/null @@ -1,1644 +0,0 @@ - array( - * 'label' => 'Delete', - * 'icon' => 'framework/images/delete.gif', - * 'icon_disabled' => 'framework/images/delete_disabled.gif', - * 'class' => 'deletelink', - * ) - * ) - */ - public $actions = array( - 'delete' => array( - 'label' => 'Delete', - 'icon' => 'framework/images/delete.gif', - 'icon_disabled' => 'framework/images/delete_disabled.gif', - 'class' => 'deletelink' - ) - ); - - /** - * @var $defaultAction String Action being executed when clicking on table-row (defaults to "show"). - * Mostly needed in ComplexTableField-subclass. - */ - public $defaultAction = ''; - - /** - * @var $customCsvQuery Query for CSV-export (might need different fields or further filtering) - */ - protected $customCsvQuery; - - /** - * Character to seperate exported columns in the CSV file - */ - protected $csvSeparator = ","; - - /* - * Boolean deciding whether to include a header row in the CSV file - */ - protected $csvHasHeader = true; - - /** - * @var array Specify custom escape for the fields. - * - * - * array("\""=>"\"\"","\r"=>"", "\r\n"=>"", "\n"=>"") - * - */ - public $csvFieldEscape = array( - "\""=>"\"\"", - "\r\n"=>"", - "\r"=>"", - "\n"=>"", - ); - - /** - * @var boolean Trigger pagination - */ - protected $showPagination = false; - - /** - * @var string Override the {@link Link()} method - * for all pagination. Useful to force rendering of the field - * in a different context. - */ - public $paginationBaseLink = null; - - /** - * @var int Number of items to show on a single page (needed for pagination) - */ - protected $pageSize = 10; - - /** - * @var array Definitions for highlighting table-rows with a specific class. You can use all column-names - * in the result of a query. Use in combination with {@setCustomQuery} to select custom properties and joined - * objects. - * - * Example: - * array( - * array( - * "rule" => '$Flag == "red"', - * "class" => "red" - * ), - * array( - * "rule" => '$Flag == "orange"', - * "class" => "orange" - * ) - * ) - */ - public $highlightConditions = array(); - - /** - * @var array Specify castings with fieldname as the key, and the desired casting as value. - * Example: array("MyCustomDate"=>"Date","MyShortText"=>"Text->FirstSentence") - */ - public $fieldCasting = array(); - - /** - * @var array Specify custom formatting for fields, e.g. to render a link instead of pure text. - * Caution: Make sure to escape special php-characters like in a normal php-statement. - * Example: "myFieldName" => '$ID' - */ - public $fieldFormatting = array(); - - public $csvFieldFormatting = array(); - - /** - * @var string - */ - public $exportButtonLabel = 'Export as CSV'; - - /** - * @var string $groupByField Used to group by a specific column in the DataObject - * and create partial summaries. - */ - public $groupByField = null; - - /** - * @var array - */ - protected $extraLinkParams; - - protected $__cachedQuery; - - /** - * This is a flag that enables some backward-compatibility helpers. - */ - private $getDataListFromForm; - - /** - * @param $name string The fieldname - * @param $sourceClass string The source class of this field - * @param $fieldList array An array of field headings of Fieldname => Heading Text (eg. heading1) - * @param $sourceFilter string The filter field you wish to limit the objects by (eg. parentID) - * @param $sourceSort string - * @param $sourceJoin string - */ - public function __construct($name, $sourceClass = null, $fieldList = null, $sourceFilter = null, - $sourceSort = null, $sourceJoin = null) { - - if($sourceClass) { - // You can optionally pass a list - if($sourceClass instanceof SS_List) { - $this->dataList = $sourceClass; - - } else { - $this->dataList = DataObject::get($sourceClass)->where($sourceFilter)->sort($sourceSort); - if($sourceJoin) $this->dataList = $this->dataList->join($sourceJoin); - // Grab it from the form relation, if available. - $this->getDataListFromForm = true; - } - } - - $this->fieldList = ($fieldList) ? $fieldList : singleton($this->sourceClass())->summaryFields(); - - $this->readOnly = false; - - parent::__construct($name); - } - - public function index() { - return $this->FieldHolder(); - } - - static $url_handlers = array( - 'item/$ID' => 'handleItem', - '$Action' => '$Action', - ); - - public function sourceClass() { - $list = $this->getDataList(); - if(method_exists($list, 'dataClass')) return $list->dataClass(); - // Failover for SS_List - else return get_class($list->First()); - } - - public function handleItem($request) { - return new TableListField_ItemRequest($this, $request->param('ID')); - } - - public function FieldHolder($properties = array()) { - Requirements::javascript(FRAMEWORK_DIR . '/thirdparty/jquery/jquery.js'); - Requirements::javascript(FRAMEWORK_DIR . '/thirdparty/prototype/prototype.js'); - Requirements::javascript(FRAMEWORK_DIR . '/thirdparty/behaviour/behaviour.js'); - Requirements::add_i18n_javascript(FRAMEWORK_DIR . '/javascript/lang'); - Requirements::javascript(FRAMEWORK_DIR . '/javascript/TableListField.js'); - Requirements::css(FRAMEWORK_DIR . '/css/TableListField.css'); - - if($this->clickAction) { - $id = $this->id(); - Requirements::customScript(<<clickAction - return false; - } - } - }); -JS - );} - - $obj = $properties ? $this->customise($properties) : $this; - return $obj->renderWith($this->template); - } - - public function Headings() { - $headings = array(); - foreach($this->fieldList as $fieldName => $fieldTitle) { - $isSorted = (isset($_REQUEST['ctf'][$this->getName()]['sort']) - && $fieldName == $_REQUEST['ctf'][$this->getName()]['sort']); - - // we can't allow sorting with partial summaries (groupByField) - $isSortable = ($this->form && $this->isFieldSortable($fieldName) && !$this->groupByField); - - // sorting links (only if we have a form to refresh with) - if($this->form) { - $sortLink = $this->Link(); - $sortLink = HTTP::setGetVar("ctf[{$this->getName()}][sort]", $fieldName, $sortLink,'&'); - - // Apply sort direction to the current sort field - if(!empty($_REQUEST['ctf'][$this->getName()]['sort']) - && ($_REQUEST['ctf'][$this->getName()]['sort'] == $fieldName)) { - - if(isset($_REQUEST['ctf'][$this->getName()]['dir'])) { - $dir = $_REQUEST['ctf'][$this->getName()]['dir']; - } else { - $dir = null; - } - - $dir = trim(strtolower($dir)); - $newDir = ($dir == 'desc') ? null : 'desc'; - $sortLink = HTTP::setGetVar("ctf[{$this->getName()}][dir]", Convert::raw2xml($newDir), - $sortLink,'&'); - } - - if(isset($_REQUEST['ctf'][$this->getName()]['search']) - && is_array($_REQUEST['ctf'][$this->getName()]['search'])) { - - foreach($_REQUEST['ctf'][$this->getName()]['search'] as $parameter => $value) { - $XML_search = Convert::raw2xml($value); - $sortLink = HTTP::setGetVar("ctf[{$this->getName()}][search][$parameter]", $XML_search, - $sortLink,'&'); - } - } - } else { - $sortLink = '#'; - } - - $headings[] = new ArrayData(array( - "Name" => $fieldName, - "Title" => ($this->sourceClass()) - ? singleton($this->sourceClass())->fieldLabel($fieldTitle) - : $fieldTitle, - "IsSortable" => $isSortable, - "SortLink" => $sortLink, - "SortBy" => $isSorted, - "SortDirection" => (isset($_REQUEST['ctf'][$this->getName()]['dir'])) - ? $_REQUEST['ctf'][$this->getName()]['dir'] - : null - )); - } - return new ArrayList($headings); - } - - public function disableSorting($to = true) { - $this->disableSorting = $to; - } - - /** - * Determines if a field is "sortable". - * If the field is generated by a custom getter, we can't sort on it - * without generating all objects first (which would be a huge performance impact). - * - * @param string $fieldName - * @return bool - */ - public function isFieldSortable($fieldName) { - if($this->disableSorting) return false; - $list = $this->getDataList(); - if(method_exists($list,'canSortBy')) return $list->canSortBy($fieldName); - else return false; - } - - /** - * Dummy function to get number of actions originally generated in - * TableListField_Item. - * - * @return SS_List - */ - public function Actions() { - $allowedActions = new ArrayList(); - foreach($this->actions as $actionName => $actionSettings) { - if($this->Can($actionName)) { - $allowedActions->push(new ViewableData()); - } - } - - return $allowedActions; - } - - /** - * Provide a custom query to compute sourceItems. This is the preferred way to using - * {@setSourceItems}, because we can still paginate. - * Please use this only as a fallback for really complex queries (e.g. involving HAVING and GROUPBY). - * - * @param $query DataList - */ - public function setCustomQuery(DataList $dataList) { - $this->dataList = $dataList; - return $this; - } - - public function setCustomCsvQuery(DataList $dataList) { - $this->customCsvQuery = $query; - return $this; - } - - public function setCustomSourceItems(SS_List $items) { - user_error('TableList::setCustomSourceItems() deprecated, just pass the items into the constructor', - E_USER_WARNING); - - // The type-hinting above doesn't seem to work consistently - if($items instanceof SS_List) { - $this->dataList = $items; - } else { - user_error('TableList::setCustomSourceItems() should be passed a SS_List', E_USER_WARNING); - } - - return $this; - } - - /** - * Get items, with sort & limit applied - */ - public function sourceItems() { - // get items (this may actually be a SS_List) - $items = clone $this->getDataList(); - - // TODO: Sorting could be implemented on regular SS_Lists. - if(method_exists($items,'canSortBy') && isset($_REQUEST['ctf'][$this->getName()]['sort'])) { - $sort = $_REQUEST['ctf'][$this->getName()]['sort']; - // TODO: sort direction - if($items->canSortBy($sort)) $items = $items->sort($sort); - } - - // Determine pagination limit, offset - // To disable pagination, set $this->showPagination to false. - if($this->showPagination && $this->pageSize) { - $SQL_limit = (int)$this->pageSize; - if(isset($_REQUEST['ctf'][$this->getName()]['start']) - && is_numeric($_REQUEST['ctf'][$this->getName()]['start'])) { - - if(isset($_REQUEST['ctf'][$this->getName()]['start'])) { - $SQL_start = intval($_REQUEST['ctf'][$this->getName()]['start']); - } else { - $SQL_start = "0"; - } - } else { - $SQL_start = 0; - } - - $items = $items->limit($SQL_limit, $SQL_start); - } - - return $items; - } - - /** - * Return a SS_List of TableListField_Item objects, suitable for display in the template. - */ - public function Items() { - $fieldItems = new ArrayList(); - if($items = $this->sourceItems()) foreach($items as $item) { - if($item) $fieldItems->push(new $this->itemClass($item, $this)); - } - return $fieldItems; - } - - /** - * Returns the DataList for this field. - */ - public function getDataList() { - // If we weren't passed in a DataList to begin with, try and get the datalist from the form - if($this->form && $this->getDataListFromForm) { - $this->getDataListFromForm = false; - $relation = $this->name; - if($record = $this->form->getRecord()) { - if($record->hasMethod($relation)) $this->dataList = $record->$relation(); - } - } - - if(!$this->dataList) { - user_error(get_class($this). ' is missing a DataList', E_USER_ERROR); - } - - return $this->dataList; - } - - public function getCsvDataList() { - if($this->customCsvQuery) return $this->customCsvQuery; - else return $this->getDataList(); - } - - /** - * @deprecated Use getDataList() instead. - */ - public function getQuery() { - Deprecation::notice('3.0', 'Use getDataList() instead.'); - $list = $this->getDataList(); - if(method_exists($list,'dataQuery')) { - return $this->getDataList()->dataQuery()->query(); - } - } - - /** - * @deprecated Use getCsvDataList() instead. - */ - public function getCsvQuery() { - Deprecation::notice('3.0', 'Use getCsvDataList() instead.'); - $list = $this->getCsvDataList(); - if(method_exists($list,'dataQuery')) { - return $list->dataQuery()->query(); - } - } - - public function FieldList() { - return $this->fieldList; - } - - /** - * Configure this table to load content into a subform via ajax - */ - public function setClick_AjaxLoad($urlBase, $formID) { - $this->clickAction = "this.ajaxRequest('" . addslashes($urlBase) . "', '" . addslashes($formID) . "')"; - return $this; - } - - /** - * Configure this table to open a popup window - */ - public function setClick_PopupLoad($urlBase) { - $this->clickAction = "var w = window.open(baseHref()+'$urlBase'+this.id.replace(/.*-(\d*)$/,'$1'), 'popup');" - . " w.focus();"; - return $this; - } - - public function performReadonlyTransformation() { - $clone = clone $this; - $clone->setShowPagination(false); - - // Only include the show action if it was in the original CTF. - $clone->setPermissions(in_array('show', $this->permissions) ? array('show') : array()); - - $clone->addExtraClass( 'readonly' ); - $clone->setReadonly(true); - return $clone; - } - - /** - * ################################# - * CRUD - * ################################# - */ - - /** - * @return String - */ - public function delete($request) { - // Protect against CSRF on destructive action - $token = $this->getForm()->getSecurityToken(); - if(!$token->checkRequest($request)) return $this->httpError('400'); - - if($this->Can('delete') !== true) { - return false; - } - - $this->methodName = "delete"; - - $childId = Convert::raw2sql($_REQUEST['ctf']['childID']); - - if (is_numeric($childId)) { - $this->getDataList()->removeById($childId); - } - - // TODO return status in JSON etc. - //return $this->renderWith($this->template); - } - - - /** - * ################################# - * Summary-Row - * ################################# - */ - - /** - * Can utilize some built-in summary-functions, with optional casting. - * Currently supported: - * - sum - * - avg - * - * @param $summaryTitle string - * @param $summaryFields array - * Simple Format: array("MyFieldName"=>"sum") - * With Casting: array("MyFieldname"=>array("sum","Currency->Nice")) - */ - public function addSummary($summaryTitle, $summaryFieldList) { - $this->summaryTitle = $summaryTitle; - $this->summaryFieldList = $summaryFieldList; - } - - public function removeSummary() { - $this->summaryTitle = null; - $this->summaryFields = null; - } - - public function HasSummary() { - return (isset($this->summaryFieldList)); - } - - public function SummaryTitle() { - return $this->summaryTitle; - } - - /** - * @param SS_List $items Only used to pass grouped sourceItems for creating - * partial summaries. - */ - public function SummaryFields($items = null) { - if(!isset($this->summaryFieldList)) { - return false; - } - $summaryFields = array(); - $fieldListWithoutFirst = $this->fieldList; - if(!empty($this->summaryTitle)) { - array_shift($fieldListWithoutFirst); - } - foreach($fieldListWithoutFirst as $fieldName => $fieldTitle) { - - if(in_array($fieldName, array_keys($this->summaryFieldList))) { - if(is_array($this->summaryFieldList[$fieldName])) { - $summaryFunction = "colFunction_{$this->summaryFieldList[$fieldName][0]}"; - $casting = $this->summaryFieldList[$fieldName][1]; - } else { - $summaryFunction = "colFunction_{$this->summaryFieldList[$fieldName]}"; - $casting = null; - } - - // fall back to integrated sourceitems if not passed - if(!$items) $items = $this->sourceItems(); - - $summaryValue = ($items) ? $this->$summaryFunction($items->column($fieldName)) : null; - - // Optional casting, Format: array('MyFieldName'=>array('sum','Currency->Nice')) - if(isset($casting)) { - $summaryValue = $this->getCastedValue($summaryValue, $casting); - } - } else { - $summaryValue = null; - $function = null; - } - - $summaryFields[] = new ArrayData(array( - 'Function' => $function, - 'SummaryValue' => $summaryValue, - 'Name' => DBField::create_field('Varchar', $fieldName), - 'Title' => DBField::create_field('Varchar', $fieldTitle), - )); - } - return new ArrayList($summaryFields); - } - - public function HasGroupedItems() { - return ($this->groupByField); - } - - public function GroupedItems() { - if(!$this->groupByField) { - return false; - } - - $items = $this->sourceItems(); - if(!$items || !$items->Count()) { - return false; - } - - $groupedItems = $items->groupBy($this->groupByField); - $groupedArrItems = new ArrayList(); - foreach($groupedItems as $key => $group) { - $fieldItems = new ArrayList(); - foreach($group as $item) { - if($item) $fieldItems->push(new $this->itemClass($item, $this)); - } - $groupedArrItems->push(new ArrayData(array( - 'Items' => $fieldItems, - 'SummaryFields' => $this->SummaryFields($group) - ))); - } - - return $groupedArrItems; - } - - public function colFunction_sum($values) { - return array_sum($values); - } - - public function colFunction_avg($values) { - return array_sum($values)/count($values); - } - - - /** - * ################################# - * Permissions - * ################################# - */ - - /** - * Template accessor for Permissions. - * See {@link TableListField_Item->Can()} for object-specific - * permissions. - * - * @return boolean - */ - public function Can($mode) { - if($mode == 'add' && $this->isReadonly()) { - return false; - } else if($mode == 'delete' && $this->isReadonly()) { - return false; - } else if($mode == 'edit' && $this->isReadonly()) { - return false; - } else { - return (in_array($mode, $this->permissions)); - } - - } - - public function setPermissions($arr) { - $this->permissions = $arr; - return $this; - } - - /** - * @return array - */ - public function getPermissions() { - return $this->permissions; - } - - /** - * ################################# - * Pagination - * ################################# - */ - public function setShowPagination($bool) { - $this->showPagination = (bool)$bool; - return $this; - } - - /** - * @return boolean - */ - public function ShowPagination() { - if($this->showPagination && !empty($this->summaryFieldList)) { - user_error("You can't combine pagination and summaries - please disable one of them.", E_USER_ERROR); - } - return $this->showPagination; - } - - public function setPageSize($pageSize) { - $this->pageSize = $pageSize; - return $this; - } - - public function PageSize() { - return $this->pageSize; - } - - public function ListStart() { - return $_REQUEST['ctf'][$this->getName()]['start']; - } - - /** - * @param array - * @deprecated Put the query string onto your form's link instead :-) - */ - public function setExtraLinkParams($params){ - Deprecation::notice('2.4', 'Put the query string onto your FormAction instead().'); - $this->extraLinkParams = $params; - return $this; - } - - /** - * @return array - */ - public function getExtraLinkParams(){ - return $this->extraLinkParams; - } - - public function FirstLink() { - $start = 0; - - if(!isset($_REQUEST['ctf'][$this->getName()]['start']) - || !is_numeric($_REQUEST['ctf'][$this->getName()]['start']) - || $_REQUEST['ctf'][$this->getName()]['start'] == 0) { - - return null; - } - $baseLink = ($this->paginationBaseLink) ? $this->paginationBaseLink : $this->Link(); - $link = Controller::join_links($baseLink, "?ctf[{$this->getName()}][start]={$start}"); - if($this->extraLinkParams) $link .= "&" . http_build_query($this->extraLinkParams); - - // preserve sort options - if(isset($_REQUEST['ctf'][$this->getName()]['sort'])) { - $link .= "&ctf[{$this->getName()}][sort]=" . $_REQUEST['ctf'][$this->getName()]['sort']; - // direction - if(isset($_REQUEST['ctf'][$this->getName()]['dir'])) { - $link .= "&ctf[{$this->getName()}][dir]=" . $_REQUEST['ctf'][$this->getName()]['dir']; - } - } - - return $link; - } - - public function PrevLink() { - if(isset($_REQUEST['ctf'][$this->getName()]['start'])) { - $currentStart = $_REQUEST['ctf'][$this->getName()]['start']; - } else { - $currentStart = 0; - } - - if($currentStart == 0) { - return null; - } - - if($_REQUEST['ctf'][$this->getName()]['start'] - $this->pageSize < 0) { - $start = 0; - } else { - $start = $_REQUEST['ctf'][$this->getName()]['start'] - $this->pageSize; - } - - $baseLink = ($this->paginationBaseLink) ? $this->paginationBaseLink : $this->Link(); - $link = Controller::join_links($baseLink, "?ctf[{$this->getName()}][start]={$start}"); - if($this->extraLinkParams) $link .= "&" . http_build_query($this->extraLinkParams); - - // preserve sort options - if(isset($_REQUEST['ctf'][$this->getName()]['sort'])) { - $link .= "&ctf[{$this->getName()}][sort]=" . $_REQUEST['ctf'][$this->getName()]['sort']; - // direction - if(isset($_REQUEST['ctf'][$this->getName()]['dir'])) { - $link .= "&ctf[{$this->getName()}][dir]=" . $_REQUEST['ctf'][$this->getName()]['dir']; - } - } - - return $link; - } - - - public function NextLink() { - $currentStart = isset($_REQUEST['ctf'][$this->getName()]['start']) - ? $_REQUEST['ctf'][$this->getName()]['start'] - : 0; - $start = ($currentStart + $this->pageSize < $this->TotalCount()) - ? $currentStart + $this->pageSize - : $this->TotalCount() % $this->pageSize > 0; - if($currentStart >= $start-1) { - return null; - } - $baseLink = ($this->paginationBaseLink) ? $this->paginationBaseLink : $this->Link(); - $link = Controller::join_links($baseLink, "?ctf[{$this->getName()}][start]={$start}"); - if($this->extraLinkParams) $link .= "&" . http_build_query($this->extraLinkParams); - - // preserve sort options - if(isset($_REQUEST['ctf'][$this->getName()]['sort'])) { - $link .= "&ctf[{$this->getName()}][sort]=" . $_REQUEST['ctf'][$this->getName()]['sort']; - // direction - if(isset($_REQUEST['ctf'][$this->getName()]['dir'])) { - $link .= "&ctf[{$this->getName()}][dir]=" . $_REQUEST['ctf'][$this->getName()]['dir']; - } - } - - return $link; - } - - public function LastLink() { - $pageSize = ($this->TotalCount() % $this->pageSize > 0) - ? $this->TotalCount() % $this->pageSize - : $this->pageSize; - $start = $this->TotalCount() - $pageSize; - // Check if there is only one page, or if we are on last page - if($this->TotalCount() <= $pageSize || (isset($_REQUEST['ctf'][$this->getName()]['start']) - && $_REQUEST['ctf'][$this->getName()]['start'] >= $start)) { - - return null; - } - - $baseLink = ($this->paginationBaseLink) ? $this->paginationBaseLink : $this->Link(); - $link = Controller::join_links($baseLink, "?ctf[{$this->getName()}][start]={$start}"); - if($this->extraLinkParams) $link .= "&" . http_build_query($this->extraLinkParams); - - // preserve sort options - if(isset($_REQUEST['ctf'][$this->getName()]['sort'])) { - $link .= "&ctf[{$this->getName()}][sort]=" . $_REQUEST['ctf'][$this->getName()]['sort']; - // direction - if(isset($_REQUEST['ctf'][$this->getName()]['dir'])) { - $link .= "&ctf[{$this->getName()}][dir]=" . $_REQUEST['ctf'][$this->getName()]['dir']; - } - } - - return $link; - } - - public function FirstItem() { - if ($this->TotalCount() < 1) return 0; - return isset($_REQUEST['ctf'][$this->getName()]['start']) - ? $_REQUEST['ctf'][$this->getName()]['start'] + 1 - : 1; - } - - public function LastItem() { - if(isset($_REQUEST['ctf'][$this->getName()]['start'])) { - $pageStep = min($this->pageSize, $this->TotalCount() - $_REQUEST['ctf'][$this->getName()]['start']); - return $_REQUEST['ctf'][$this->getName()]['start'] + $pageStep; - } else { - return min($this->pageSize, $this->TotalCount()); - } - } - - /** - * @ignore - */ - private $_cache_TotalCount; - - /** - * Return the total number of items in the source DataList - */ - public function TotalCount() { - if($this->_cache_TotalCount === null) { - $this->_cache_TotalCount = $this->getDataList()->Count(); - } - return $this->_cache_TotalCount; - } - - - /** - * ################################# - * Search - * ################################# - * - * @todo Not fully implemented at the moment - */ - - /** - * Compile all request-parameters for search and pagination - * (except the actual list-positions) as a query-string. - * - * @return String URL-parameters - */ - public function filterString() { - - } - - - - /** - * ################################# - * CSV Export - * ################################# - */ - public function setFieldListCsv($fields) { - $this->fieldListCsv = $fields; - return $this; - } - - /** - * Set the CSV separator character. Defaults to , - */ - public function setCsvSeparator($csvSeparator) { - $this->csvSeparator = $csvSeparator; - return $this; - } - - /** - * Get the CSV separator character. Defaults to , - */ - public function getCsvSeparator() { - return $this->csvSeparator; - } - - /** - * Remove the header row from the CSV export - */ - public function removeCsvHeader() { - $this->csvHasHeader = false; - return $this; - } - - /** - * Exports a given set of comma-separated IDs (from a previous search-query, stored in a HiddenField). - * Uses {$csv_columns} if present, and falls back to {$result_columns}. - * We move the most filedata generation code to the function {@link generateExportFileData()} so that a child class - * could reuse the filedata generation code while overwrite export function. - * - * @todo Make relation-syntax available (at the moment you'll have to use custom sql) - */ - public function export() { - $now = date("d-m-Y-H-i"); - $fileName = "export-$now.csv"; - - // No pagination for export - $oldShowPagination = $this->showPagination; - $this->showPagination = false; - - $result = $this->renderWith(array($this->template . '_printable', 'TableListField_printable')); - - $this->showPagination = $oldShowPagination; - - if($fileData = $this->generateExportFileData($numColumns, $numRows)){ - return SS_HTTPRequest::send_file($fileData, $fileName, 'text/csv'); - } else { - user_error("No records found", E_USER_ERROR); - } - } - - public function generateExportFileData(&$numColumns, &$numRows) { - $separator = $this->csvSeparator; - $csvColumns = ($this->fieldListCsv) ? $this->fieldListCsv : $this->fieldList; - $fileData = ''; - $columnData = array(); - $fieldItems = new ArrayList(); - - if($this->csvHasHeader) { - $fileData .= "\"" . implode("\"{$separator}\"", array_values($csvColumns)) . "\""; - $fileData .= "\n"; - } - - if(isset($this->customSourceItems)) { - $items = $this->customSourceItems; - } else { - $items = $this->getCsvDataList(); - } - - // temporary override to adjust TableListField_Item behaviour - $this->setFieldFormatting(array()); - $this->fieldList = $csvColumns; - - if($items) { - foreach($items as $item) { - if(is_array($item)) { - $className = isset($item['RecordClassName']) ? $item['RecordClassName'] : $item['ClassName']; - $item = new $className($item); - } - $fieldItem = new $this->itemClass($item, $this); - - $fields = $fieldItem->Fields(false); - $columnData = array(); - if($fields) foreach($fields as $field) { - $value = $field->Value; - - // TODO This should be replaced with casting - if(array_key_exists($field->Name, $this->csvFieldFormatting)) { - $format = str_replace('$value', "__VAL__", $this->csvFieldFormatting[$field->Name]); - $format = preg_replace('/\$([A-Za-z0-9-_]+)/','$item->$1', $format); - $format = str_replace('__VAL__', '$value', $format); - eval('$value = "' . $format . '";'); - } - - $value = str_replace(array("\r", "\n"), "\n", $value); - $tmpColumnData = '"' . str_replace('"', '\"', $value) . '"'; - $columnData[] = $tmpColumnData; - } - $fileData .= implode($separator, $columnData); - $fileData .= "\n"; - - $item->destroy(); - unset($item); - unset($fieldItem); - } - - $numColumns = count($columnData); - $numRows = $fieldItems->count(); - return $fileData; - } else { - return null; - } - } - - /** - * We need to instanciate this button manually as a normal button has no means of adding inline onclick-behaviour. - */ - public function ExportLink() { - $exportLink = Controller::join_links($this->Link(), 'export'); - - if($this->extraLinkParams) $exportLink .= "?" . http_build_query($this->extraLinkParams); - return $exportLink; - } - - public function printall() { - Requirements::clear(); - if(defined('CMS_DIR')) { - Requirements::css(CMS_DIR . '/css/typography.css'); - Requirements::css(CMS_DIR . '/css/cms_right.css'); - } - - $this->cachedSourceItems = null; - $oldShowPagination = $this->showPagination; - $this->showPagination = false; - - increase_time_limit_to(); - $this->Print = true; - - $result = $this->renderWith(array($this->template . '_printable', 'TableListField_printable')); - - $this->showPagination = $oldShowPagination; - - return $result; - } - - public function PrintLink() { - $link = Controller::join_links($this->Link(), 'printall'); - if(isset($_REQUEST['ctf'][$this->getName()]['sort'])) { - $link = HTTP::setGetVar("ctf[{$this->getName()}][sort]", - Convert::raw2xml($_REQUEST['ctf'][$this->getName()]['sort']), $link); - } - return $link; - } - - /** - * ################################# - * Utilty - * ################################# - */ - public function Utility() { - $links = new ArrayList(); - if($this->can('export')) { - $links->push(new ArrayData(array( - 'Title' => _t('TableListField.CSVEXPORT', 'Export to CSV'), - 'Link' => $this->ExportLink() - ))); - } - if($this->can('print')) { - $links->push(new ArrayData(array( - 'Title' => _t('TableListField.PRINT', 'Print'), - 'Link' => $this->PrintLink() - ))); - } - return $links; - - } - - public function setFieldCasting($casting) { - $this->fieldCasting = $casting; - return $this; - } - - public function setFieldFormatting($formatting) { - $this->fieldFormatting = $formatting; - return $this; - } - - public function setCSVFieldFormatting($formatting) { - $this->csvFieldFormatting = $formatting; - return $this; - } - - /** - * Edit the field list - */ - public function setFieldList($fieldList) { - $this->fieldList = $fieldList; - return $this; - } - - /** - * @return String - */ - public function Name() { - return $this->name; - } - - public function Title() { - // adding translating functionality - // this is a bit complicated, because this parameter is passed to this class - // and should come here translated already - // adding this to TODO probably add a method to the classes - // to return they're translated string - // added by ruibarreiros @ 27/11/2007 - return $this->sourceClass() ? singleton($this->sourceClass())->singular_name() : $this->getName(); - } - - public function NameSingular() { - // same as Title() - // added by ruibarreiros @ 27/11/2007 - return $this->sourceClass() ? singleton($this->sourceClass())->singular_name() : $this->getName(); - } - - public function NamePlural() { - // same as Title() - // added by ruibarreiros @ 27/11/2007 - return $this->sourceClass() ? singleton($this->sourceClass())->plural_name() : $this->getName(); - } - - public function setTemplate($template) { - $this->template = $template; - return $this; - } - - public function CurrentLink() { - $link = $this->Link(); - - if(isset($_REQUEST['ctf'][$this->getName()]['start']) - && is_numeric($_REQUEST['ctf'][$this->getName()]['start'])) { - - $start = ($_REQUEST['ctf'][$this->getName()]['start'] < 0) - ? 0 - : $_REQUEST['ctf'][$this->getName()]['start']; - $link = Controller::join_links($link, "?ctf[{$this->getName()}][start]={$start}"); - } - - if($this->extraLinkParams) $link .= "&" . http_build_query($this->extraLinkParams); - - return $link; - } - - /** - * Overloaded to automatically add security token. - * - * @param String $action - * @return String - */ - public function Link($action = null) { - $form = $this->getForm(); - if($form) { - $token = $form->getSecurityToken(); - $parentUrlParts = parse_url(parent::Link($action)); - $queryPart = (isset($parentUrlParts['query'])) ? '?' . $parentUrlParts['query'] : null; - // Ensure that URL actions not routed through Form->httpSubmission() are protected against CSRF attacks. - if($token->isEnabled()) $queryPart = $token->addtoUrl($queryPart); - return Controller::join_links($parentUrlParts['path'], $queryPart); - } else { - // allow for instanciation of this FormField outside of a controller/form - // context (e.g. for unit tests) - return false; - } - } - - public function BaseLink() { - user_error("TableListField::BaseLink() deprecated, use Link() instead", E_USER_NOTICE); - return $this->Link(); - } - - /** - * Helper method to determine permissions for a scaffolded - * TableListField (or subclasses) - currently used in {@link ModelAdmin} and - * {@link DataObject->scaffoldFormFields()}. - * - * Returns true for each permission that doesn't have an explicit getter. - * - * @todo Temporary method, implement directly in FormField subclasses with object-level permissions. - * - * @param string $class - * @param numeric $id - * @return array - */ - public static function permissions_for_object($class, $id = null) { - $permissions = array(); - $obj = ($id) ? DataObject::get_by_id($class, $id) : singleton($class); - - if(!$obj->hasMethod('canView') || $obj->canView()) $permissions[] = 'show'; - if(!$obj->hasMethod('canEdit') || $obj->canEdit()) $permissions[] = 'edit'; - if(!$obj->hasMethod('canDelete') || $obj->canDelete()) $permissions[] = 'delete'; - if(!$obj->hasMethod('canCreate') || $obj->canCreate()) $permissions[] = 'add'; - - return $permissions; - } - - /** - * @param $value - * - */ - public function getCastedValue($value, $castingDefinition) { - if(is_array($castingDefinition)) { - $castingParams = $castingDefinition; - array_shift($castingParams); - $castingDefinition = array_shift($castingDefinition); - } else { - $castingParams = array(); - } - if(strpos($castingDefinition,'->') === false) { - $castingFieldType = $castingDefinition; - $castingField = DBField::create_field($castingFieldType, $value); - $value = call_user_func_array(array($castingField,'XML'),$castingParams); - } else { - $fieldTypeParts = explode('->', $castingDefinition); - $castingFieldType = $fieldTypeParts[0]; - $castingMethod = $fieldTypeParts[1]; - $castingField = DBField::create_field($castingFieldType, $value); - $value = call_user_func_array(array($castingField,$castingMethod),$castingParams); - } - - return $value; - } - - public function setHighlightConditions($conditions) { - $this->highlightConditions = $conditions; - return $this; - } - - /** - * See {@link SelectOptions()} for introduction. - * - * @param $options array Options to add, key being a unique identifier of the action, - * and value a title for the rendered link element (can contain HTML). - * The keys for 'all' and 'none' have special behaviour associated - * through TableListField.js JavaScript. - * For any other key, the JavaScript automatically checks all checkboxes contained in - * elements with a matching classname. - */ - public function addSelectOptions($options){ - foreach($options as $k => $title) - $this->selectOptions[$k] = $title; - } - - /** - * Remove one all more table's {@link $selectOptions} - * - * @param $optionsNames array - */ - public function removeSelectOptions($names){ - foreach($names as $name){ - unset($this->selectOptions[trim($name)]); - } - } - - /** - * Return the table's {@link $selectOptions}. - * Used to toggle checkboxes for each table row through button elements. - * - * Requires {@link Markable()} to return TRUE. - * This is only functional with JavaScript enabled. - * - * @return SS_List of ArrayData objects - */ - public function SelectOptions(){ - if(!$this->selectOptions) return; - - $selectOptionsSet = new ArrayList(); - foreach($this->selectOptions as $k => $v) { - $selectOptionsSet->push(new ArrayData(array( - 'Key' => $k, - 'Value' => $v - ))); - } - return $selectOptionsSet; - } -} - -/** - * A single record in a TableListField. - * @package forms - * @subpackage fields-relational - * @see TableListField - */ -class TableListField_Item extends ViewableData { - - /** - * @var DataObject The underlying data record, - * usually an element of {@link TableListField->sourceItems()}. - */ - protected $item; - - /** - * @var TableListField - */ - protected $parent; - - public function __construct($item, $parent) { - $this->failover = $this->item = $item; - $this->parent = $parent; - parent::__construct(); - } - - public function ID() { - return $this->item->ID; - } - - public function Parent() { - return $this->parent; - } - - public function Fields($xmlSafe = true) { - $list = $this->parent->FieldList(); - foreach($list as $fieldName => $fieldTitle) { - $value = ""; - - // This supports simple FieldName syntax - if(strpos($fieldName,'.') === false) { - $value = ($this->item->XML_val($fieldName) && $xmlSafe) - ? $this->item->XML_val($fieldName) - : $this->item->RAW_val($fieldName); - // This support the syntax fieldName = Relation.RelatedField - } else { - $fieldNameParts = explode('.', $fieldName) ; - $tmpItem = $this->item; - for($j=0;$j$relationMethod; - } else { - if($tmpItem) $tmpItem = $tmpItem->$relationMethod(); - } - } - } - - // casting - if(array_key_exists($fieldName, $this->parent->fieldCasting)) { - $value = $this->parent->getCastedValue($value, $this->parent->fieldCasting[$fieldName]); - } elseif(is_object($value) && method_exists($value, 'Nice')) { - $value = $value->Nice(); - } - - // formatting - $item = $this->item; - if(array_key_exists($fieldName, $this->parent->fieldFormatting)) { - $format = str_replace('$value', "__VAL__", $this->parent->fieldFormatting[$fieldName]); - $format = preg_replace('/\$([A-Za-z0-9-_]+)/','$item->$1', $format); - $format = str_replace('__VAL__', '$value', $format); - eval('$value = "' . $format . '";'); - } - - //escape - if($escape = $this->parent->fieldEscape){ - foreach($escape as $search => $replace){ - $value = str_replace($search, $replace, $value); - } - } - - $fields[] = new ArrayData(array( - "Name" => $fieldName, - "Title" => $fieldTitle, - "Value" => $value, - "CsvSeparator" => $this->parent->getCsvSeparator(), - )); - } - return new ArrayList($fields); - } - - public function Markable() { - return $this->parent->Markable; - } - - /** - * Checks global permissions for field in {@link TableListField->Can()}. - * If they are allowed, it checks for object permissions by assuming - * a method with "can" + $mode parameter naming, e.g. canDelete(). - * - * @param string $mode See {@link TableListField::$permissions} array. - * @return boolean - */ - public function Can($mode) { - $canMethod = "can" . ucfirst($mode); - if(!$this->parent->Can($mode)) { - // check global settings for the field instance - return false; - } elseif($this->item->hasMethod($canMethod)) { - // if global allows, check object specific permissions (e.g. canDelete()) - return $this->item->$canMethod(); - } else { - // otherwise global allowed this action, so return TRUE - return true; - } - } - - public function Link($action = null) { - $form = $this->parent->getForm(); - if($form) { - $token = $form->getSecurityToken(); - $parentUrlParts = parse_url($this->parent->Link()); - $queryPart = (isset($parentUrlParts['query'])) ? '?' . $parentUrlParts['query'] : null; - // Ensure that URL actions not routed through Form->httpSubmission() are protected against CSRF attacks. - if($token->isEnabled()) $queryPart = $token->addtoUrl($queryPart); - return Controller::join_links($parentUrlParts['path'], 'item', $this->item->ID, $action, $queryPart); - } else { - // allow for instanciation of this FormField outside of a controller/form - // context (e.g. for unit tests) - return false; - } - } - - /** - * Returns all row-based actions not disallowed through permissions. - * See TableListField->Action for a similiar dummy-function to work - * around template-inheritance issues. - * - * @return SS_List - */ - public function Actions() { - $allowedActions = new ArrayList(); - foreach($this->parent->actions as $actionName => $actionSettings) { - if($this->parent->Can($actionName)) { - $allowedActions->push(new ArrayData(array( - 'Name' => $actionName, - 'Link' => $this->{ucfirst($actionName).'Link'}(), - 'Icon' => $actionSettings['icon'], - 'IconDisabled' => $actionSettings['icon_disabled'], - 'Label' => $actionSettings['label'], - 'Class' => $actionSettings['class'], - 'Default' => ($actionName == $this->parent->defaultAction), - 'IsAllowed' => $this->Can($actionName), - ))); - } - } - - return $allowedActions; - } - - public function BaseLink() { - user_error("TableListField_Item::BaseLink() deprecated, use Link() instead", E_USER_NOTICE); - return $this->Link(); - } - - public function DeleteLink() { - return Controller::join_links($this->Link(), "delete"); - } - - public function MarkingCheckbox() { - $name = $this->parent->getName() . '[]'; - - if($this->parent->isReadonly()) - return "item->ID}\"" - . " disabled=\"disabled\" />"; - else - return "item->ID}\" />"; - } - - /** - * According to {@link TableListField->selectOptions}, each record will check if the options' key on the object is - * true, if it is true, add the key as a class to the record - * - * @return string Value for a 'class' HTML attribute. - */ - public function SelectOptionClasses(){ - $tagArray = array('markingcheckbox'); - $options = $this->parent->SelectOptions(); - if($options && $options->exists()){ - foreach($options as $option){ - if($option->Key !== 'all' && $option->Key !== 'none'){ - if($this->{$option->Key}) { - $tagArray[] = $option->Key; - } - } - } - } - return implode(" ",$tagArray); - } - - public function HighlightClasses() { - $classes = array(); - foreach($this->parent->highlightConditions as $condition) { - $rule = str_replace("\$","\$this->item->", $condition['rule']); - $ruleApplies = null; - eval('$ruleApplies = ('.$rule.');'); - if($ruleApplies) { - if(isset($condition['exclusive']) && $condition['exclusive']) { - return $condition['class']; - } else { - $classes[] = $condition['class']; - } - } - } - - return (count($classes) > 0) ? " " . implode(" ", $classes) : false; - } - - /** - * Legacy: Please use permissions instead - */ - public function isReadonly() { - return $this->parent->Can('delete'); - } -} - -/** - * @package forms - * @subpackage fields-relational - */ -class TableListField_ItemRequest extends RequestHandler { - protected $ctf; - protected $itemID; - protected $methodName; - - static $url_handlers = array( - '$Action!' => '$Action', - '' => 'index', - ); - - public function Link() { - return Controller::join_links($this->ctf->Link(), 'item/' . $this->itemID); - } - - public function __construct($ctf, $itemID) { - $this->ctf = $ctf; - $this->itemID = $itemID; - - parent::__construct(); - } - - public function delete($request) { - // Protect against CSRF on destructive action - $token = $this->ctf->getForm()->getSecurityToken(); - if(!$token->checkRequest($request)) return $this->httpError('400'); - - if($this->ctf->Can('delete') !== true) { - return false; - } - - $this->dataObj()->delete(); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * Return the data object being manipulated - */ - public function dataObj() { - // used to discover fields if requested and for population of field - if(is_numeric($this->itemID)) { - // we have to use the basedataclass, otherwise we might exclude other subclasses - return $this->ctf->getDataList()->byId($this->itemID); - } - - } - - /** - * @return TableListField - */ - public function getParentController() { - return $this->ctf; - } -} - diff --git a/javascript/ComplexTableField.js b/javascript/ComplexTableField.js deleted file mode 100644 index 1ffc8f7d6..000000000 --- a/javascript/ComplexTableField.js +++ /dev/null @@ -1,156 +0,0 @@ -// Shortcut-function (until we update to Prototye v1.5) -if(typeof $$ != "Function") $$ = document.getElementsBySelector; - -GB_OpenerObj = {}; -GB_RefreshLink = ""; - -ComplexTableField = Class.create(); -ComplexTableField.prototype = { - - // These are defaults used if setPopupSize encounters errors - defaultPopupWidth: 560, - defaultPopupHeight: 390, - - initialize: function() { - var rules = {}; - rules['#'+this.id+' table.data a.popuplink'] = {onclick: this.openPopup.bind(this)}; - - // Assume that the delete link uses the deleteRecord method - rules['#'+this.id+' table.data a.deletelink'] = {onclick: this.deleteRecord.bind(this)}; - - // invoke row action-link based on default-action set in classname - var defaultAction = this.getDefaultAction(); - - if(defaultAction) { - rules['#'+this.id+' table.data tbody td'] = { - onclick: function(e) { - var elt = Event.element(e); - // Check the tag, as otherwise this - // function can take over checkbox - // click actions etc. See ticket #4737 - if (elt.tagName != 'TD' && elt.tagName != 'TR') { - return; - } - - var link = $$('.'+defaultAction, Event.element(e).parentNode)[0].href; - this.openPopup(null, link); - return false; - }.bind(this) - }; - } - Behaviour.register('ComplexTableField_'+this.id,rules); - - this.setPopupSize(); - - // HACK If already in a popup, we can't allow add (doesn't save existing relation correctly) - if(window != top) $$('#'+this.id+' table.data a.addlink').each(function(el) {Element.hide(el);}); - }, - - setPopupSize: function() { - try { - this.popupHeight = parseInt(document.getElementById(this.id + '_PopupHeight').value); - this.popupWidth = parseInt(document.getElementById(this.id + '_PopupWidth').value); - } catch (ex) { - this.popupHeight = this.defaultPopupHeight; - this.popupWidth = this.defaultPopupWidth; - } - }, - - getDefaultAction: function() { - // try to get link class from 0) { - return true; - } - - try { - var table = Event.findElement(e,"table"); - if(Event.element(e).nodeName == "IMG") { - link = Event.findElement(e,"a"); - popupLink = link.href + (link.href.match(/\?/) ? "&ajax=1" : "?ajax=1"); - } else { - el = Event.findElement(e,"tr"); - var link = $$("a",el)[0]; - popupLink = link.href; - } - } catch(err) { - // no link found - Event.stop(e); - return false; - } - // no link found - if(!link || popupLink.length == 0) { - Event.stop(e); - return false; - } - } - - if(this.GB_Caption) { - var title = this.GB_Caption; - } else { - // Getting the title from the URL is pretty ugly, but it works for now - type = popupLink.match(/[0-9]+\/([^\/?&]*)([?&]|$)/); - var title = (type && type[1]) ? type[1].ucfirst() : ""; - } - - // reset internal greybox callbacks, they are not properly unregistered - // and fire multiple times on each subsequent popup close action otherwise - if(GB_ONLY_ONE) GB_ONLY_ONE.callback_fn = []; - - GB_show( - title, - popupLink, - this.popupHeight, - this.popupWidth, - this.refresh.bind(this) - ); - - if(e) { - Event.stop(e); - } - return false; - } -} - -ComplexTableField.applyTo('div.ComplexTableField'); - -/** - * Get first letter as uppercase - */ -String.prototype.ucfirst = function () { - var firstLetter = this.substr(0,1).toUpperCase() - return this.substr(0,1).toUpperCase() + this.substr(1,this.length); -} \ No newline at end of file diff --git a/javascript/ComplexTableField_popup.js b/javascript/ComplexTableField_popup.js deleted file mode 100644 index fa104f9b0..000000000 --- a/javascript/ComplexTableField_popup.js +++ /dev/null @@ -1,11 +0,0 @@ -ComplexTableFieldPopupForm = Class.create(); -ComplexTableFieldPopupForm.prototype = { - - initialize: function() { - var rules = {}; - - Behaviour.register(rules); - } -} -ComplexTableFieldPopupForm.applyTo('#ComplexTableField_Popup_DetailForm'); -ComplexTableFieldPopupForm.applyTo('#ComplexTableField_Popup_AddForm'); \ No newline at end of file diff --git a/javascript/HasManyFileField.js b/javascript/HasManyFileField.js deleted file mode 100644 index 9c7b1bebc..000000000 --- a/javascript/HasManyFileField.js +++ /dev/null @@ -1,137 +0,0 @@ -HasManyFileField = Class.create(); -HasManyFileField.applyTo('div.hasmanyfile'); -HasManyFileField.prototype = { - initialize: function() { - HasManyFileFieldAddButton.applyToChildren(this, 'a.addFile'); - HasManyFileFieldRemoveButton.applyToChildren(this, 'a.removeFile'); - HasManyFileFieldUploadButton.applyToChildren(this, 'a.uploadFile'); - this.tree = document.getElementsByClassName('TreeDropdownField', this)[0]; - this.list = this.getElementsByTagName('ul')[0]; - this.uploadFolderID = this.getElementsByTagName('input')[0].value; - this.uploadMessage = document.getElementsByClassName('uploadMessage')[0]; - } -} - -HasManyFileFieldAddButton = Class.create(); -HasManyFileFieldAddButton.prototype = { - onclick: function() { - tree = this.parentNode.parentNode.tree; - list = this.parentNode.parentNode.list; - fieldid = this.parentNode.parentNode.id; - fileid = tree.getElementsByTagName('input')[0].value; - name = tree.getElementsByTagName('span')[0].innerHTML; - - input = document.createElement('input'); - input.className = 'hidden'; - input.type = 'hidden'; - input.name = fieldid + '[]'; - input.value = fileid; - - text = document.createTextNode(name); - link = document.createElement('a'); - link.appendChild(text); - - removelink = document.createElement('a'); - removelink.className = 'removeFile'; - removelink.innerHTML = 'Remove file'; - - li = document.createElement('li'); - li.appendChild(input); - li.appendChild(link); - li.appendChild(removelink); - list.appendChild(li); - - HasManyFileFieldRemoveButton.applyTo(removelink); - - return false; - } -} - -HasManyFileFieldRemoveButton = Class.create(); -HasManyFileFieldRemoveButton.prototype = { - onclick: function() { - li = this.parentNode; - list = this.parentNode.parentNode; - list.removeChild(li); - - return false; - } -} - -HasManyFileFieldUploadButton = Class.create(); -HasManyFileFieldUploadButton.prototype = { - initialize: function() { - this.upload = new Upload({ - fileUploadLimit : '6', - securityID : document.getElementById('SecurityID').value, - beginUploadOnQueue : true, - fileQueued : this.uploadFileQueuedCallback.bind(this), - fileComplete : this.uploadFileCompleteCallback.bind(this), - queueComplete : this.uploadQueueCompleteCallback.bind(this) - }); - - this.upload.setFolderID(this.parentNode.parentNode.uploadFolderID); - }, - - buildUI: function() { - - }, - - onclick: function(event) { - Event.stop(event); - this.upload.browse(); - }, - - uploadFileQueuedCallback: function(file,queueLength) { - var message = ss.i18n.sprintf( - ss.i18n._t('HASMANYFILEFIELD.UPLOADING', 'Uploading... %s'), - this.upload.getFilesToUpload() - ); - this.parentNode.parentNode.uploadMessage.innerHTML = message; - }, - - uploadFileCompleteCallback: function(file,serverData) { - var message = ss.i18n.sprintf( - ss.i18n._t('HASMANYFILEFIELD.UPLOADING', 'Uploading... %s'), - this.upload.getFilesUploaded() + "/" + this.upload.getFilesToUpload() - ); - this.parentNode.parentNode.uploadMessage.innerHTML = message; - idregex = /\/\* IDs: ([0-9,]+) \*\//; - ids = serverData.match(idregex); - fileid = ids[1]; - - nameregex = /\/\* Names: ([^\s]+) \*\//; - names = serverData.match(nameregex); - name = names[1]; - - fieldid = this.parentNode.parentNode.id; - list = this.parentNode.parentNode.list; - - input = document.createElement('input'); - input.className = 'hidden'; - input.type = 'hidden'; - input.name = fieldid + '[]'; - input.value = fileid; - - text = document.createTextNode(name); - link = document.createElement('a'); - link.appendChild(text); - - removelink = document.createElement('a'); - removelink.className = 'removeFile'; - removelink.innerHTML = 'Remove file'; - - li = document.createElement('li'); - li.appendChild(input); - li.appendChild(link); - li.appendChild(removelink); - list.appendChild(li); - - HasManyFileFieldRemoveButton.applyTo(removelink); - }, - - uploadQueueCompleteCallback: function() { - this.parentNode.parentNode.uploadMessage.innerHTML = ''; - } -} - diff --git a/javascript/RelationComplexTableField.js b/javascript/RelationComplexTableField.js deleted file mode 100644 index 61131348f..000000000 --- a/javascript/RelationComplexTableField.js +++ /dev/null @@ -1,117 +0,0 @@ - - -Event.observe( window, 'load', function() { - if(document.getElementById('sitetree')){ - if(typeof document.getElementById('sitetree').observeMethod != 'undefined') { - document.getElementById('sitetree').observeMethod( 'NodeClicked' , function() { - checkedListNameArray = null; - checkedArray = null; - } ); - } - } -} ); - -RelationComplexTableField = Class.create(); -RelationComplexTableField.prototype = { - initialize: function() { - var checkedListNameArray = null; - var checkedListEndName = 'CheckedList'; - var checkedListField = 'selected'; - var checkedArray = null; - - // 1) Find The Hidden Field Where The IDs Will Be Stored - - var checkedList = document.getElementById( this.id + '_' + checkedListEndName ); - - // 2) Initialize The Array Or Update The Hidden Input Field And The HTML Table - - var checkedListName = checkedList.getAttribute( 'name' ); - //if( checkedListNameArray == null ) { - checkedListNameArray = []; - checkedListNameArray.push( checkedListName ); - checkedArray = []; - if( checkedList.getAttribute( 'value' ) ) - checkedArray.push( checkedList.getAttribute( 'value' ).split( ',' ) ); - //} - /* - else if( checkedListNameArray.indexOf( checkedListName ) < 0 ) { - checkedListNameArray.push( checkedListName ); - if( checkedList.getAttribute( 'value' ) ) - checkedArray[ checkedListNameArray.length - 1 ] = checkedList.getAttribute( 'value' ).split( ',' ); - } - else { - - var index = checkedListNameArray.indexOf( checkedListName ); - - // a) Update The Hidden Input Field - - checkedList.setAttribute( 'value', checkedArray[ index ] ); - - // b) Update The HTML Table - - markingInputs = document.getElementsByName( checkedListName.substring( 0, checkedListName.indexOf( '[' ) ) + '[]' ); - - for( var i = 0; i < markingInputs.length; i++ ) { - markingInput = markingInputs[ i ]; - if( checkedArray[ index ] && checkedArray[ index ].indexOf( markingInput.getAttribute( 'value' ) ) > -1 ) { - markingInput.setAttribute( 'checked', 'checked' );} - else - markingInput.removeAttribute( 'checked' ); - } - } */ - - // 3) Create The Rules - - var rules = {}; - - rules[ '#' + this.id + ' table.data tbody td.markingcheckbox input' ] = { - onclick : function() { - - // 1) Find The Hidden Field Where The IDs Will Be Stored - - var checkedListName = this.getAttribute( 'name' ); - checkedListName = checkedListName.substring( 0, checkedListName.length - 1 ) + checkedListField + ']'; - var inputs = document.getElementsByTagName( 'input' ); - for( var i = 0; i < inputs.length; i++ ) { - var checkedList = inputs[ i ]; - if( checkedList.getAttribute( 'name' ) == checkedListName ) - break; - } - var index = checkedListNameArray.indexOf( checkedListName ); - - // 2) Update The Array - - if( checkedArray[ index ] && checkedArray[ index ].indexOf( this.getAttribute( 'value' ) ) > -1 ) { - index2 = checkedArray[ index ].indexOf( this.getAttribute( 'value' ) ); - var previousCheckedArray = checkedArray[ index ]; - checkedArray[ index ] = []; - for( var i = 0; i < previousCheckedArray.length; i++ ) { - if( i != index2 ) - checkedArray[ index ].push( previousCheckedArray[ i ] ); - } - if( this.getAttribute( 'type' ) == 'radio' ) - this.checked = false; - } - else if( checkedArray[ index ] ) { - if( this.getAttribute( 'type' ) == 'radio' ) - checkedArray[ index ] = []; - checkedArray[ index ].push( this.getAttribute( 'value' ) ); - } - else { - checkedArray[ index ] = []; - checkedArray[ index ].push( this.getAttribute( 'value' ) ); - } - - // 3) Update The Hidden Input Field - - checkedList.setAttribute( 'value', checkedArray[ index ] ); - } - }; - - Behaviour.register( rules ); - } -} - -RelationComplexTableField.applyTo('#Form_EditForm div.HasOneComplexTableField'); -RelationComplexTableField.applyTo('#Form_EditForm div.HasManyComplexTableField'); -RelationComplexTableField.applyTo('#Form_EditForm div.ManyManyComplexTableField'); diff --git a/javascript/TableField.js b/javascript/TableField.js deleted file mode 100644 index d78bf440f..000000000 --- a/javascript/TableField.js +++ /dev/null @@ -1,191 +0,0 @@ -/** - * Javascript for TableField. allows the deletion of records via - * AJAX, and the addition of rows via javasript. - * - * TODO relies on include-order at the moment to override actions :/ - */ -TableField = Class.create(); -TableField.prototype = { - - newRowID: 1, - - /** - * Applies behaviour to the delete button for deleting objects via ajax. - */ - initialize: function() { - var rules = {}; - - rules['#'+this.id+' table.data a.deletelink'] = { - onclick: this.deleteRecord.bind(this) - }; - - rules['#'+this.id+' table.data a.addrow'] = { - onclick: this.addRow.bind(this) - }; - - Behaviour.register('TableField_'+this.id,rules); - }, - - /** - * Deletes the given dataobject record via an ajax request. If the record doesn't have any - * information in it, it just removes it from the form. - * to tablefield->Delete() - * @param {Object} e - */ - deleteRecord: function(e) { - var img = Event.element(e); - var link = Event.findElement(e,"a"); - var row = Event.findElement(e,"tr"); - var params = link.getAttribute("href").toQueryParams(); - var isEmpty = true; - var recordID = row.getRecordId(); - var self = this; - - // Check to see if there is a dataobject to delete first, otherwise remove the row. - // or: Check if a childID is set (not present on new items) - if( - (this.hasNoValues(row,"input") && this.hasNoValues(row,"select") && this.hasNoValues(row,"textarea")) - || params["childID"] <= 0 || (recordID <= 0 || recordID == false) - ){ - if( row.parentNode.getElementsByTagName('tr').length > 1 ) { - jQuery(row).fadeOut(); - } else { - // clear all fields in the row - var fields = row.getElementsByTagName('input'); - if( fields ) - for( var i = 0; i < fields.length; i++ ) { - fields[i].value = ''; - } - } - Event.stop(e); - return false; - } - - // TODO ajaxErrorHandler and loading-image are dependent on cms, but formfield is in framework - var confirmed = confirm(ss.i18n._t('TABLEFIELD.DELETECONFIRMMESSAGE', 'Are you sure you want to delete this record?')); - if(confirmed){ - img.setAttribute("src",'framework/images/network-save.gif'); // TODO doesn't work - jQuery.ajax({ - 'url': link.getAttribute("href"), - 'method': 'post', - 'data': {ajax: 1, 'SecurityID': document.getElementById('SecurityID') ? document.getElementById('SecurityID').value : null}, - 'success': function(response){ - jQuery(row).fadeOut('fast', function() { - // remove row from DOM - this.element.parentNode.removeChild(obj.element); - // recalculate summary if needed (assumes that TableListField.js is present) - // TODO Proper inheritance - if(self._summarise) self._summarise(); - // custom callback - if(self.callback_deleteRecord) self.callback_deleteRecord(e); - }); - }, - 'error': ajaxErrorHandler - }); - } - Event.stop(e); - return false; - }, - - /** - * - * - * @param {Object} element - * @param {Object} tagName - */ - hasNoValues: function(element,tagName){ - elements = element.getElementsByTagName(tagName); - - if(elements.length >= 1){ - var isEmpty = true; - for(var i = 0; i < elements.length;i++){ - if(elements[i].type != "hidden"){ - if(elements[i].value != null && elements[i].value != ""){ - isEmpty = false; - } - } - } - - return isEmpty; - }else{ - return true; - } - }, - - - /** - * Appends a new row to the DOM. - * - * @param {Object} tableid - */ - addRow: function (e){ - - var table = Event.findElement(e,"table"); - if(table){ - // Clone the last TR - var tbody = table.tBodies[0]; - var numRows = tbody.rows.length; - var newRow = tbody.rows[0].cloneNode(true); - - // Get the input elements in this new row - var inputs = newRow.getElementsByTagName('input'); - // For every input, set it's value to blank if it is not hidden - for(var i = 0; i < inputs.length; i++) { - if(inputs[i].type != 'hidden') { - inputs[i].value = "" - }; - } - - this.newRowID++; - - if(newRow.id != "new"){ - this.resetNames(newRow); - } - - // Make sure all inputs have unique IDs - for(var i = 0; i < inputs.length; i++) { - inputs[i].id += "-" + this.newRowID; - } - - // Change the ID to a unique one - newRow.id = "New_" + this.newRowID; - - // Append the new row to the DOM - table.tBodies[0].appendChild(newRow); - Behaviour.apply(table); - } - Event.stop(e); - }, - - /** - * resets the names for all elements inside a row. - * @param {Object} row - */ - resetNames: function(row){ - - // Support for addressing the ID's appropriately. - for(i = 0; i < row.cells.length;i++){ - for(b=0; b < row.cells[i].childNodes.length;b++){ - inputElement = row.cells[i].childNodes[b]; - if(inputElement.type != 'hidden') inputElement.value = ""; - if(inputElement.name != null){ - if(inputElement.name.substr(inputElement.name.length - 2,inputElement.name.length) != "[]"){ - inputElement.name = - inputElement.name.substr(0,inputElement.name.indexOf('[')+1) + "new" + - inputElement.name.substr(inputElement.name.indexOf(']'),inputElement.name.length) + "[]"; - }else{ - inputElement.name = - inputElement.name.substr(0,inputElement.name.indexOf('[')+1) + "new" + - inputElement.name.substr(inputElement.name.indexOf(']'),inputElement.name.length); - } - } - } - } - } -} -TableField.applyTo('div.TableField'); -if(typeof ajaxErrorHandler == 'undefined'){ - ajaxErrorHandler = function(response) { - alert(response.responseText); - } -} diff --git a/javascript/TableListField.js b/javascript/TableListField.js deleted file mode 100644 index e2fcbe88d..000000000 --- a/javascript/TableListField.js +++ /dev/null @@ -1,374 +0,0 @@ -// Shortcut-function (until we update to Prototye v1.5) -if(typeof $$ != "Function") $$ = document.getElementsBySelector; - -TableListField = Class.create(); -TableListField.prototype = { - - errorMessage: "Error talking to server", - - initialize: function() { - var rules = {}; - - rules['#'+this.id+' table.data a.deletelink'] = { - onclick: this.deleteRecord.bind(this) - }; - - rules['#'+this.id+' th a'] = { - onclick: this.refresh.bind(this) - }; - - rules['#'+this.id+' th'] = { - initialize: function() { - var sortLinks = $$('span.sortLinkHidden a', this); - if(sortLinks) sortLinks[0].style.visibility = 'hidden'; - }, - onmouseover: function(e) { - var sortLinks = $$('span.sortLinkHidden a', this); - if(sortLinks) sortLinks[0].style.visibility = 'visible'; - }, - onmouseout: function(e) { - var sortLinks = $$('span.sortLinkHidden a', this); - if(sortLinks) sortLinks[0].style.visibility = 'hidden'; - } - }; - - rules['#'+this.id+' div.PageControls a'] = {onclick: this.refresh.bind(this)}; - - rules['#'+this.id+' table.data tr td.markingcheckbox'] = { - onclick : function(e) { - // do nothing for clicks in marking box cells (e.g. if checkbox is missed) - } - }; - - // rules for selection options on click event - rules['#'+this.id+' .selectOptions a'] = { - onclick: this.markRecords.bind(this) - }; - - // initialize summary (if needed) - // TODO Breaks with nested divs - var summaryCols = $$('tfoot tr.summary td', this); - this._summaryDefs = []; - - //should check summaryCols.length, because summaryCols will always be an array, though its length could be 0. - if(summaryCols && summaryCols.length) { - rules['#'+this.id+' table.data tbody input'] = { - onchange: function(e) { - if (!e) e = window.event; // stupid IE - // workaround for wrong scope with bind(this) and applyTo() - var root = Event.findElement(e,'div'); - // TODO Fix slow $$()-calls and re-enable clientside summaries - //root._summarise(); - } - }; - rules['#'+this.id+' table.data tbody select'] = { - onchange: function(e) { - if (!e) e = window.event; // stupid IE - // workaround for wrong scope with bind(this) and applyTo() - var root = Event.findElement(e,'div'); - // TODO Fix slow $$()-calls and re-enable clientside summaries - //root._summarise(); - }.bind(this) - }; - } - - Behaviour.register('TableListField_'+this.id,rules); - - /* - if(summaryCols.length) { - this._getSummaryDefs(summaryCols); - } - */ - }, - - /** - * Deletes the given dataobject record via an ajax request - * to complextablefield->Delete() - * @param {Object} e - */ - deleteRecord: function(e) { - var img = Event.element(e); - var link = Event.findElement(e,"a"); - var row = Event.findElement(e,"tr"); - var self = this; - - // TODO ajaxErrorHandler and loading-image are dependent on cms, but formfield is in framework - var confirmed = confirm(ss.i18n._t('TABLEFIELD.DELETECONFIRMMESSAGE', 'Are you sure you want to delete this record?')); - if(confirmed) - { - img.setAttribute("src",'framework/images/network-save.gif'); // TODO doesn't work - jQuery.ajax({ - 'url': link.getAttribute("href"), - 'method': 'post', - 'data': {forceajax: 1, SecurityID: jQuery('input[name=SecurityID]').val()}, - 'success': function(){ - jQuery(row).remove(); - // recalculate summary if needed (assumes that TableListField.js is present) - // TODO Proper inheritance - if(self._summarise) self._summarise(); - // custom callback - if(self.callback_deleteRecord) self.callback_deleteRecord(e); - }, - 'error': this.ajaxErrorHandler - }); - } - Event.stop(e); - }, - - removeById: function(id) { - var el = jQuery('#record-' + this.id + '-' + id)[0]; - if(el) el.parentNode.removeChild(el); - this._summarise(); - }, - - /** - * according to the clicked element in "Select bar", mark records that have same class as the element. - */ - markRecords: function(e){ - var el = Event.element(e); - if(el.nodeName != "a") el = Event.findElement(e,"a"); - - if(el.rel == "all"){ - this.markAll(); - }else if(el.rel == 'none') { - this.unmarkAll(); - }else{ - this.unmarkAll(); - var records = jQuery('#' + this.id + ' td.' + el.rel + ' input.checkbox'); - var i=0; - for(i; i - Behaviour.apply(jQuery('#' + self.id)[0], true); - } - }); - } - - if(e) Event.stop(e); - return false; - }, - - ajaxErrorHandler: function(response) { - if(typeof(window.ajaxErrorHandler) == 'function') { - window.ajaxErrorHandler(); - } else { - alert(this.errorMessage); - } - }, - - _getSummaryDefs: function(summaryCols) { - summaryCols.each(function(col, pos) { - if( col ) { - var func = this._getSummaryFunction(col.className); - this._summaryDefs[pos] = {col: col, pos: pos, func: func}; - } - }.bind(this)); - - this._summarise(); - }, - - _summarise: function() { - var rows = $$('tbody tr', this); - if(!rows) return false; - - var columnData = []; - // prepare the array (gets js-errors otherwise) - var cols = $$('td', rows[0]); - for(colPos=0; colPos(Allowed extensions: *.csv)' SilverStripeNavigator: Edit: Edit - SimpleImageField: - NOUPLOAD: 'No Image Uploaded' SiteTree: TABMAIN: Main - TableField: - ISREQUIRED: 'In %s ''%s'' is required' - TableField.ss: - ADD: 'Add a new row' - ADDITEM: 'Add %s' TableListField: CSVEXPORT: 'Export to CSV' - PRINT: Print Print: Print SELECT: 'Select:' - TableListField.ss: - NOITEMSFOUND: 'No items found' - SORTASC: 'Sort in ascending order' - SORTDESC: 'Sort in descending order' TableListField_PageControls.ss: DISPLAYING: Displaying OF: of diff --git a/scss/ComplexTableField.scss b/scss/ComplexTableField.scss deleted file mode 100755 index b4b9a1bb2..000000000 --- a/scss/ComplexTableField.scss +++ /dev/null @@ -1,16 +0,0 @@ -/* table */ -.ComplexTableField { - margin-bottom: 10px; - - tbody td { - cursor: pointer; - } - - tbody td.markingcheckbox { - cursor: default; - } -} - -.ui-dialog .ctf-dialog.ui-dialog-content { - padding-right: 0; /* scrollbars */ -} \ No newline at end of file diff --git a/scss/HasManyFileField.scss b/scss/HasManyFileField.scss deleted file mode 100755 index e87a617f4..000000000 --- a/scss/HasManyFileField.scss +++ /dev/null @@ -1,124 +0,0 @@ -#right form .hasmanyfile a.addFile, -#right form .hasmanyfile a.uploadFile, -#right form .hasmanyfile .currentFiles li { - font-size: 1.2em; - padding-left: 3px; -} -#right form .hasmanyfile a.uploadFile { - border-color:#CCCCCC rgb(153, 153, 153) rgb(153, 153, 153) rgb(204, 204, 204); - border-style:solid; - border-width: 2px; - color:#333333; - cursor:pointer; - font-size:11px; - font-weight: bold; - position: relative; - top: -29px; - left: 344px; - text-decoration: none; - overflow:visible; - padding:3px 5px; - float:left; - width:auto; -} - -#right form .hasmanyfile a.removeFile { - border-color:#CCCCCC rgb(153, 153, 153) rgb(153, 153, 153) rgb(204, 204, 204); - border-style:solid; - border-width: 2px; - color:#333333; - cursor:pointer; - font-size:10px; - font-weight: bold; - text-decoration: none; - overflow:visible; - padding:3px 5px; - width:auto; - margin: -2px 0 0 10px; -} -#right form .hasmanyfile a.removeFile:hover { - background: #CE0000; - color: #fff; -} -#right form .hasmanyfile a.addFile { - border-color:#CCCCCC rgb(153, 153, 153) rgb(153, 153, 153) rgb(204, 204, 204); - border-style:solid; - border-width: 2px; - color:#333333; - cursor:pointer; - font-size:11px; - font-weight: bold; - position: relative; - top: -29px; - left: 340px; - text-decoration: none; - overflow:visible; - padding:3px 5px; - float:left; - width:auto; -} - #right form .hasmanyfile a.addFile:hover, - #right form .hasmanyfile a.uploadFile:hover { - background: #fff; - } -#right form .hasmanyfile ul.currentFiles { - padding-bottom: 5px; -} - #right form .hasmanyfile .currentFiles li { - height: 30px; - line-height: 30px; - } -#right form .hasmanyfile .clear { - clear: both; -} -/* ICONS */ -#right form .hasmanyfile .currentFiles a[href$=".pdf"], -#right form .hasmanyfile .currentFiles a[href$=".PDF"], -#right form .hasmanyfile .currentFiles a.pdf { - padding: 2px; - padding-left: 20px; - background: url(../images/icons/page_white_acrobat.png) no-repeat left center; -} -#right form .hasmanyfile .currentFiles a[href$=".doc"], -#right form .hasmanyfile .currentFiles a[href$=".DOC"], -#right form .hasmanyfile .currentFiles a.doc { - padding: 2px; - padding-left: 20px; - background: url(../images/icons/page_word.png) no-repeat left center; -} -#right form .hasmanyfile .currentFiles a[href$=".xls"], -#right form .hasmanyfile .currentFiles a[href$=".XLS"], -#right form .hasmanyfile .currentFiles a.xls { - padding: 2px; - padding-left: 20px; - background: url(../images/icons/page_excel.png) no-repeat left center; -} -#right form .hasmanyfile .currentFiles a[href$=".gz"], -#right form .hasmanyfile .currentFiles a[href$=".GZ"], -#right form .hasmanyfile .currentFiles a[href$=".gzip"], -#right form .hasmanyfile .currentFiles a[href$=".GZIP"], -#right form .hasmanyfile .currentFiles a[href$=".zip"], -#right form .hasmanyfile .currentFiles a[href$=".ZIP"], -#right form .hasmanyfile .currentFiles a.archive { - padding: 2px; - padding-left: 20px; - background: url(../images/icons/page_white_zip.png) no-repeat left center; -} -#right form .hasmanyfile .currentFiles a[href$=".jpg"], -#right form .hasmanyfile .currentFiles a[href$=".JPG"], -#right form .hasmanyfile .currentFiles a[href$=".gif"], -#right form .hasmanyfile .currentFiles a[href$=".GIF"], -#right form .hasmanyfile .currentFiles a[href$=".png"], -#right form .hasmanyfile .currentFiles a[href$=".PNG"], -#right form .hasmanyfile .currentFiles a.image { - padding: 2px; - padding-left: 20px; - background: url(../images/icons/icon-jpg.gif) no-repeat left center; -} -#right form .hasmanyfile .currentFiles a[href$=".exe"], -#right form .hasmanyfile .currentFiles a[href$=".EXE"], -#right form .hasmanyfile .currentFiles a.application { - padding: 2px; - padding-left: 20px; - background: url(../images/icons/application.png) no-repeat left center; -} diff --git a/scss/TableListField.scss b/scss/TableListField.scss deleted file mode 100755 index 981f26894..000000000 --- a/scss/TableListField.scss +++ /dev/null @@ -1,226 +0,0 @@ -table.TableField, -table.TableListField, -.TableListField table.data, -table.CMSList { - border-collapse: collapse; - border-spacing: 0; - width : 100%; -} - -/* Preventing IE6 from showing double borders */ -body>div table.TableField, -body>div table.TableListField, -body>div .TableListField table.data, -body>div table.CMSList { - border-collapse: separate; -} - -table.TableField td, -table.TableListField td, -.TableListField table.data td, -table.CMSList td { - border-style:none; -} - -table.TableField th, -table.TableListField th, -.TableListField table.data th, -table.CMSList th { - white-space: nowrap; -} - -table.TableField thead th, -.TableListField table.data thead th, -table.CMSList thead th { - white-space: nowrap; - padding: 3px; - font-size: 12px; - text-align: left; -} - -table.TableField thead th span, -.TableListField table.data thead th span { - font-size: 12px; -} - -table.TableField thead th span.sortLink, -.TableListField table.data thead th span.sortLink, -table.CMSList thead th span.sortLink { - overflow: hidden; -} - -table.TableField tbody td, -table.TableField tfoot td, -.TableListField table.data tbody td, -.TableListField table.data tfoot td, -table.CMSList tbody td, -table.CMSList tfoot td { - padding: 2px 4px; -} - -.TableListField table.data tfoot tr.addtogrouprow td { - padding:3px; -} - -.TableListField table.data tfoot .actions { - float: none; -} - -.TableListField table.data tfoot tr.addtogrouprow input { - width: 94%; -} - -.TableField td input, -.TableListField td input { - width: 98%; -} - -table.data tbody td input, -table.data tbody td textarea { - border:0 !important; -} - -table.TableField tbody td.checkbox, -.TableListField table.data tbody td.checkbox, -table.CMSList tbody td.checkbox { - padding-left : 5px; - background-image : url(../images/checkbox.png); - background-repeat : repeat-x; - background-position : left bottom; -} - -.TableListField table.data tfoot .addlink img { - vertical-align: middle; - margin: 3px 6px 3px 3px; -} - -.TableListField table.data tfoot tr td a { - text-decoration: none; -} - -.TableListField table.data tbody tr td a:hover, -.TableListField table.data tfoot tr td a:hover { - background: none; -} - -/** - * Show a loading indication on a TableListField row - */ -.TableListField tr.loading td.first { - padding-left: 22px; - background: url(../images/network-save.gif) 3px 2px no-repeat; -} - -.right form .TableField span.readonly { - border: 0; - background: none; - padding: 0; - margin-bottom: 0; -} - -.right form .TableListField td { - background: #fff; -} - -.TableListField div.utility { - overflow: auto; -} -.TableListField div.utility .item { - margin-top: 1em; - padding: 3px 0 6px 0; - display: block; - float: left; -} - .TableListField div.utility a { - text-decoration: none; - color: #333; - cursor: pointer; - font-size: 11px; - margin-right: 2px; - overflow: visible; - padding: 3px 2px; - width: auto; - } - -form .TableField .message { - width: auto; -} - -.TableListField .selectOptions { - overflow: auto; - font: 1.3em; - margin: 0; - padding: 0; -} - -.TableListField .selectOptions li { - float: left; - margin: 0px 5px; -} - -.TableListField .PageControls { - margin: 5px 0; - text-align:center; - display:block; - margin-bottom: 5px; - position: relative; -} -.TableListField .PageControls * { - display:inline; - vertical-align: middle; - font-weight: bold; -} -.TableListField .PageControls .Last{ - display: block; - width: 40px; - text-align: right; - position: absolute; - right: 0px; - top: 0px; -} -.TableListField .PageControls .First{ - float:left; display:block; - width:40px; text-align:left; -} - - -#Pagination { - margin-top: 10px; - margin-left: auto; - margin-right: auto; - - a { - font-size: 14px; - width: 1px; - height: 1px; - margin: 1px; - } - span { - - display: inline; - font-size: 14px; - } - div { - display: inline; - } -} -#Pagination_Next a { - text-decoration: none; -} -#Pagination_Next a div { - position: relative; - left: -20px; -} -#Pagination_Next a img { - position: relative; - top: -15px; - left: 5px; -} -#Pagination_Previous a { - text-decoration: none; -} -#Pagination_Previous a img { - position: relative; - top: -15px; - left: 35px; -} diff --git a/templates/ComplexTableField.ss b/templates/ComplexTableField.ss deleted file mode 100644 index 0a6d32ef7..000000000 --- a/templates/ComplexTableField.ss +++ /dev/null @@ -1,82 +0,0 @@ - diff --git a/templates/ComplexTableField_popup.ss b/templates/ComplexTableField_popup.ss deleted file mode 100644 index 7f0a2217f..000000000 --- a/templates/ComplexTableField_popup.ss +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - <% base_tag %> - - -
    - $DetailForm -
    - - <% if ShowPagination %> - - - <% if Paginator.PrevLink %> - - <% end_if %> - <% if xdsfdsf %> - <% else %> - - <% end_if %> - <% if Paginator.NextLink %> - - <% end_if %> - -
    - <% _t('ComplexTableField_popup.ss.PREVIOUS', 'Previous') %> - - <% loop Paginator.Pages %> - <% if active %> - $number - <% else %> - $number - <% end_if %> - <% end_loop %> - - <% _t('ComplexTableField_popup.ss.NEXT', 'Next') %> -
    - <% end_if %> - - diff --git a/templates/RelationComplexTableField.ss b/templates/RelationComplexTableField.ss deleted file mode 100644 index aa6ceae08..000000000 --- a/templates/RelationComplexTableField.ss +++ /dev/null @@ -1,67 +0,0 @@ -
    - <% if Markable %> - <% include TableListField_SelectOptions %> - <% end_if %> - <% include TableListField_PageControls %> - - - - <% if Markable %><% end_if %> - <% loop Headings %> - - <% end_loop %> - <% if Can(show) %><% end_if %> - <% if Can(edit) %><% end_if %> - <% if Can(delete) %><% end_if %> - - - - <% if HasSummary %> - - <% if Markable %><% end_if %> - - <% loop SummaryFields %> - class="$Function"<% end_if %>>  - <% end_loop %> - <% if Can(show) %><% end_if %> - <% if Can(edit) %><% end_if %> - <% if Can(delete) %><% end_if %> - - <% end_if %> - <% if Can(add) %> - - <% if Markable %><% end_if %> - - <% if Can(show) %><% end_if %> - <% if Can(edit) %><% end_if %> - <% if Can(delete) %><% end_if %> - - <% end_if %> - - - <% if Items %> - <% loop Items %> - <% include TableListField_Item %> - <% end_loop %> - <% else %> - - <% if Markable %><% end_if %> - - <% if Can(show) %><% end_if %> - <% if Can(edit) %><% end_if %> - <% if Can(delete) %><% end_if %> - - <% end_if %> - -
     $Title   
     $SummaryTitle   
      - - - <% _t('ADD', 'Add') %><% _t('RelationComplexTableField.ss.ADD', 'Add') %> $Title -    
     <% _t('RelationComplexTableField.ss.NOTFOUND', 'No items found') %>   
    - $ExtraData - -
    diff --git a/templates/TableField.ss b/templates/TableField.ss deleted file mode 100644 index 2e4a63c5d..000000000 --- a/templates/TableField.ss +++ /dev/null @@ -1,71 +0,0 @@ -
    -
    - <% include TableListField_PageControls %> - <% if Message %> -

    $Message

    - <% else %> - - <% end_if %> - - - - <% loop Headings %> - - <% end_loop %> - - <% if Can(delete) %><% end_if %> - - - - <% if HasSummary %> - - - <% loop SummaryFields %> - class="$Function"<% end_if %>>$SummaryValue - <% end_loop %> - - <% if Can(delete) %><% end_if %> - - <% end_if %> - <% if Can(add) %> - - - - <% if Can(delete) %><% end_if %> - - <% end_if %> - - - <% if Items %> - <% loop Items %> - - <% loop Fields %> - - <% end_loop %> - - <% if Can(delete) %><% end_if %> - - <% end_loop %> - <% else %> - - <% if Markable %><% end_if %> - - <% if Can(delete) %><% end_if %> - - <% end_if %> - -
    $Title 
    $SummaryTitle 
    - <% _t('TableField.ss.ADD','Add a new row') %> - <% sprintf(_t('TableField.ss.ADDITEM','Add %s'),$Title) %> - -  
    $Field$ExtraData<% _t('TableField.ss.DELETE') %>
     <% _t('TableField.ss.NOITEMSFOUND') %> 
    - <% if Print %><% else %>
    - <% loop Utility %> - $Title - <% end_loop %> -
    <% end_if %> - <% if Message %> - $Message - <% end_if %> -
    -
    diff --git a/templates/TableListField.ss b/templates/TableListField.ss deleted file mode 100644 index 57bca3803..000000000 --- a/templates/TableListField.ss +++ /dev/null @@ -1,85 +0,0 @@ -
    - <% if Print %><% else %> - <% if Markable %> - <% include TableListField_SelectOptions %> - <% end_if %> - <% include TableListField_PageControls %> - <% end_if %> - - - - <% if Markable %><% end_if %> - <% if Print %> - <% loop Headings %> - - <% end_loop %> - <% else %> - <% loop Headings %> - - <% end_loop %> - <% end_if %> - <% if Can(delete) %><% end_if %> - - - - <% if HasSummary %> - - - <% include TableListField_Summary %> - - - <% end_if %> - - - <% if HasGroupedItems %> - <% loop GroupedItems %> - <% loop Items %> - <% include TableListField_Item %> - <% end_loop %> - - <% include TableListField_Summary %> - - <% end_loop %> - <% else %> - <% if Items %> - <% loop Items %> - <% include TableListField_Item %> - <% end_loop %> - <% else %> - - <% if Markable %><% end_if %> - - <% if Can(delete) %><% end_if %> - - <% end_if %> - <% if Can(add) %> - $AddRecordAsTableRow - <% end_if %> - <% end_if %> - -
    <% if MarkableTitle %>$MarkableTitle<% else %> <% end_if %> - $Title - - <% if IsSortable %> - - $Title - - - <% if SortDirection = desc %> - <% _t('TableListField.ss.SORTDESC', 'Sort in descending order') %> - <% else %> - <% _t('TableListField.ss.SORTASC', 'Sort in ascending order') %> - <% end_if %> - -   - - <% else %> - $Title - <% end_if %> -  
     <% _t('TableListField.ss.NOITEMSFOUND','No items found') %> 
    - <% if Print %><% else %>
    - <% loop Utility %> - $Title - <% end_loop %> -
    <% end_if %> -
    diff --git a/templates/TableListField_printable.ss b/templates/TableListField_printable.ss deleted file mode 100644 index 1f6a1570c..000000000 --- a/templates/TableListField_printable.ss +++ /dev/null @@ -1,12 +0,0 @@ - - - - -<% base_tag %> -Print - - - - <% include TableListField %> - - \ No newline at end of file diff --git a/tests/forms/ComplexTableFieldTest.php b/tests/forms/ComplexTableFieldTest.php deleted file mode 100644 index b8ab08e7c..000000000 --- a/tests/forms/ComplexTableFieldTest.php +++ /dev/null @@ -1,202 +0,0 @@ -controller = new ComplexTableFieldTest_Controller(); - $this->manyManyForm = $this->controller->ManyManyForm(); - } - - public function testCorrectNumberOfRowsInTable() { - $field = $this->manyManyForm->Fields()->dataFieldByName('Players'); - $parser = new CSSContentParser($field->FieldHolder()); - - $this->assertEquals(count($parser->getBySelector('tbody tr')), 2, 'There are 2 players (rows) in the table'); - $this->assertEquals($field->Items()->Count(), 2, 'There are 2 CTF items in the SS_List'); - } - - public function testAddingManyManyNewPlayer() { - $this->logInWithPermission('ADMIN'); - - $team = DataObject::get_one('ComplexTableFieldTest_Team', "\"Name\" = 'The Awesome People'"); - - $this->post('ComplexTableFieldTest_Controller/ManyManyForm/field/Players/AddForm', array( - 'Name' => 'Bobby Joe', - 'ctf' => array( - 'ClassName' => 'ComplexTableFieldTest_Player', - 'manyManyRelation' => 'Players', - 'parentClass' => 'ComplexTableFieldTest_Team', - 'sourceID' => $team->ID - ) - )); - - $newPlayer = DataObject::get_one('ComplexTableFieldTest_Player', "\"Name\" = 'Bobby Joe'"); - $this->assertNotNull($newPlayer, 'A new ComplexTableFieldTest_Player record was created, Name = "Bobby Joe"'); - $teams = $newPlayer->getManyManyComponents('Teams'); - $this->assertEquals($teams->Count(), 1, 'Automatic many-many relation was set correctly on the new player'); - } - - public function testAddingHasManyData() { - $this->logInWithPermission('ADMIN'); - - $team = DataObject::get_one('ComplexTableFieldTest_Team', "\"Name\" = 'The Awesome People'"); - - $this->post('ComplexTableFieldTest_Controller/HasManyForm/field/Sponsors/AddForm', array( - 'Name' => 'Jim Beam', - 'ctf' => array( - 'ClassName' => 'ComplexTableFieldTest_Sponsor', - 'hasManyRelation' => 'Sponsors', - 'parentClass' => 'ComplexTableFieldTest_Team', - 'sourceID' => $team->ID - ) - )); - - $newSponsor = DataObject::get_one('ComplexTableFieldTest_Sponsor', "\"Name\" = 'Jim Beam'"); - $this->assertNotNull($newSponsor, 'A new ComplexTableFieldTest_Sponsor record was created, Name = "Jim Beam"'); - $this->assertEquals($newSponsor->TeamID, $team->ID, - 'Automatic has-many/has-one relation was set correctly on the sponsor'); - $this->assertEquals($newSponsor->getComponent('Team')->ID, $team->ID, - 'Automatic has-many/has-one relation was set correctly on the sponsor'); - - $team = DataObject::get_by_id('ComplexTableFieldTest_Team', $team->ID); - $sponsor = DataObject::get_by_id('ComplexTableFieldTest_Sponsor', $newSponsor->ID); - $this->assertEquals($newSponsor->ID, $sponsor->ID, 'The sponsor is the same as the one we added'); - $foundTeam = $sponsor->getComponent('Team'); - $this->assertEquals($team->ID, $foundTeam->ID, 'The team ID matches on the other side of the relation'); - } - -} -class ComplexTableFieldTest_Controller extends Controller { - - public function Link($action = null) { - return "ComplexTableFieldTest_Controller/$action"; - } - - public function ManyManyForm() { - $team = DataObject::get_one('ComplexTableFieldTest_Team', "\"Name\" = 'The Awesome People'"); - - $playersField = new ComplexTableField( - $this, - 'Players', - $team->Players(), - ComplexTableFieldTest_Player::$summary_fields, - 'getCMSFields' - ); - - $form = new Form( - $this, - 'ManyManyForm', - new FieldList( - new HiddenField('ID', '', $team->ID), - $playersField - ), - new FieldList( - new FormAction('doSubmit', 'Submit') - ) - ); - $form->loadDataFrom($team); - - $form->disableSecurityToken(); - - return $form; - } - - public function HasManyForm() { - $team = DataObject::get_one('ComplexTableFieldTest_Team', "\"Name\" = 'The Awesome People'"); - - $sponsorsField = new ComplexTableField( - $this, - 'Sponsors', - $team->Sponsors(), - ComplexTableFieldTest_Sponsor::$summary_fields, - 'getCMSFields' - ); - - $form = new Form( - $this, - 'HasManyForm', - new FieldList( - new HiddenField('ID', '', $team->ID), - $sponsorsField - ), - new FieldList( - new FormAction('doSubmit', 'Submit') - ) - ); - $form->loadDataFrom($team); - - $form->disableSecurityToken(); - - return $form; - } - -} -class ComplexTableFieldTest_Player extends DataObject implements TestOnly { - - public static $db = array( - 'Name' => 'Varchar(100)' - ); - - public static $many_many = array( - 'Teams' => 'ComplexTableFieldTest_Team' - ); - - public static $many_many_extraFields = array( - 'Teams' => array( - 'Role' => 'Varchar(100)', - 'Position' => "Enum('Admin,Player,Coach','Admin')", - 'DateJoined' => 'Date' - ) - ); - -} -class ComplexTableFieldTest_Team extends DataObject implements TestOnly { - - public static $db = array( - 'Name' => 'Varchar(100)' - ); - - public static $belongs_many_many = array( - 'Players' => 'ComplexTableFieldTest_Player' - ); - - public static $has_many = array( - 'Sponsors' => 'ComplexTableFieldTest_Sponsor' - ); - -} -class ComplexTableFieldTest_Sponsor extends DataObject implements TestOnly { - - public static $db = array( - 'Name' => 'Varchar(100)' - ); - - public static $has_one = array( - 'Team' => 'ComplexTableFieldTest_Team' - ); - -} diff --git a/tests/forms/ComplexTableFieldTest.yml b/tests/forms/ComplexTableFieldTest.yml deleted file mode 100644 index 3f9d1cd2c..000000000 --- a/tests/forms/ComplexTableFieldTest.yml +++ /dev/null @@ -1,16 +0,0 @@ -ComplexTableFieldTest_Player: - p1: - Name: Joe Bloggs - p2: - Name: Some Guy -ComplexTableFieldTest_Team: - t1: - Name: The Awesome People - Players: =>ComplexTableFieldTest_Player.p1,=>ComplexTableFieldTest_Player.p2 - t2: - Name: Incredible Four -ComplexTableFieldTest_Sponsor: - s1: - Name: Coca Cola - s2: - Name: Pepsi \ No newline at end of file diff --git a/tests/forms/TableFieldTest.php b/tests/forms/TableFieldTest.php deleted file mode 100644 index 764da5dc3..000000000 --- a/tests/forms/TableFieldTest.php +++ /dev/null @@ -1,263 +0,0 @@ -objFromFixture('Group','group1_no_perms'); - - $tableField = new TableField( - "Permissions", - "Permission", - array( - "Code" => 'Code', - "Arg" => 'Arg', - ), - array( - "Code" => "TextField", - "Arg" => "TextField", - ), - "GroupID", - $group->ID - ); - $form = new Form( - new TableFieldTest_Controller(), - "Form", - new FieldList($tableField), - new FieldList() - ); - - // Test Insert - - // The field starts emppty. Save some new data. - // We have replicated the array structure that the specific layout of the form generates. - $tableField->setValue(array( - 'new' => array( - 'Code' => array( - 'CustomPerm1', - 'CustomPerm2', - ), - 'Arg' => array( - '1', - '2' - ), - ), - )); - $tableField->saveInto($group); - - // Let's check that the 2 permissions entries have been saved - $permissions = $group->Permissions()->map('Arg', 'Code'); - $this->assertEquals(array( - 1 => 'CustomPerm1', - 2 => 'CustomPerm2', - ), $permissions->toArray()); - - - // Test repeated insert - $value = array(); - foreach($group->Permissions() as $permission) { - $value[$permission->ID] = array("Code" => $permission->Code, "Arg" => $permission->Arg); - } - $value['new'] = array( - 'Code' => array( - 'CustomPerm3', - ), - 'Arg' => array( - '3', - ), - ); - $tableField->setValue($value); - $tableField->saveInto($group); - - // Let's check that the 2 existing permissions entries, and the 1 new one, have been saved - $permissions = $group->Permissions()->map('Arg', 'Code'); - $this->assertEquals(array( - 1 => 'CustomPerm1', - 2 => 'CustomPerm2', - 3 => 'CustomPerm3', - ), $permissions->toArray()); - - } - - public function testEdit() { - $group = $this->objFromFixture('Group','group2_existing_perms'); - $perm1 = $this->objFromFixture('Permission', 'perm1'); - $perm2 = $this->objFromFixture('Permission', 'perm2'); - - $tableField = new TableField( - "Permissions", - "Permission", - array( - "Code" => 'Code', - "Arg" => 'Arg', - ), - array( - "Code" => "TextField", - "Arg" => "TextField", - ), - "GroupID", - $group->ID - ); - $form = new Form( - new TableFieldTest_Controller(), - "Form", - new FieldList($tableField), - new FieldList() - ); - - $this->assertEquals(2, $tableField->sourceItems()->Count()); - - // We have replicated the array structure that the specific layout of the form generates. - $tableField->setValue(array( - $perm1->ID => array( - 'Code' => 'Perm1 Modified', - 'Arg' => '101' - ), - $perm2->ID => array( - 'Code' => 'Perm2 Modified', - 'Arg' => '102' - ) - )); - $tableField->saveInto($group); - - // Let's check that the 2 permissions entries have been saved - $permissions = $group->Permissions()->map('Arg', 'Code'); - $this->assertEquals(array( - 101 => 'Perm1 Modified', - 102 => 'Perm2 Modified', - ), $permissions->toArray()); - } - - public function testDelete() { - $group = $this->objFromFixture('Group','group2_existing_perms'); - $perm1 = $this->objFromFixture('Permission', 'perm1'); - $perm2 = $this->objFromFixture('Permission', 'perm2'); - - $tableField = new TableField( - "Permissions", - "Permission", - array( - "Code" => 'Code', - "Arg" => 'Arg', - ), - array( - "Code" => "TextField", - "Arg" => "TextField", - ), - "GroupID", - $group->ID - ); - $form = new Form( - new TableFieldTest_Controller(), - "Form", - new FieldList($tableField), - new FieldList() - ); - - $this->assertContains($perm1->ID, $tableField->sourceItems()->column('ID')); - - $response = $tableField->Items()->find('ID', $perm1->ID)->delete(); - - $this->assertNotContains($perm1->ID, $tableField->sourceItems()->column('ID')); - } - - /** - * Relation auto-setting is now the only option - */ - public function testAutoRelationSettingOn() { - $o = new TableFieldTest_Object(); - $o->write(); - - $tf = new TableField( - 'HasManyRelations', - 'TableFieldTest_HasManyRelation', - array( - 'Value' => 'Value' - ), - array( - 'Value' => 'TextField' - ) - ); - - // Test with auto relation setting - $form = new Form(new TableFieldTest_Controller(), "Form", new FieldList($tf), new FieldList()); - $form->loadDataFrom($o); - - $tf->setValue(array( - 'new' => array( - 'Value' => array('one','two',) - ) - )); - - $form->saveInto($o); - $this->assertEquals(2, $o->HasManyRelations()->Count()); - } - - public function testHasItemsWhenSetAsArray() { - $tf = new TableField( - 'TestTableField', - 'TableFieldTest_HasManyRelation', - array( - 'Value' => 'Value' - ), - array( - 'Value' => 'TextField' - ) - ); - $tf->setValue(array( - 'new' => array( - 'Value' => array( - 'One', - 'Two', - ) - ) - )); - $items = $tf->Items(); - $itemsArr = $items->toArray(); - - // includes the two values and an "add" row - $this->assertEquals($items->Count(), 3); - - // first row - $this->assertEquals( - $itemsArr[0]->Fields()->fieldByName('TestTableField[new][Value][]')->Value(), - 'One' - ); - - // second row - $this->assertEquals( - $itemsArr[1]->Fields()->fieldByName('TestTableField[new][Value][]')->Value(), - 'Two' - ); - } - -} - -/** - * Stub controller - */ -class TableFieldTest_Controller extends Controller implements TestOnly { - public function Link($action = null) { - return Controller::join_links('TableFieldTest/', $action); - } -} -class TableFieldTest_Object extends DataObject implements TestOnly { - static $has_many = array( - "HasManyRelations" => 'TableFieldTest_HasManyRelation' - ); -} - -class TableFieldTest_HasManyRelation extends DataObject implements TestOnly { - static $db = array( - 'Value' => 'Text', - ); - - static $has_one = array( - 'HasOneRelation' => 'TableFieldTest_Object' - ); -} diff --git a/tests/forms/TableFieldTest.yml b/tests/forms/TableFieldTest.yml deleted file mode 100644 index b8822d23c..000000000 --- a/tests/forms/TableFieldTest.yml +++ /dev/null @@ -1,13 +0,0 @@ -Permission: - perm1: - Code: Perm1 - Arg: 1 - perm2: - Code: Perm2 - Arg: 2 -Group: - group1_no_perms: - Title: Group A - group2_existing_perms: - Title: Group B - Permissions: =>Permission.perm1,=>Permission.perm2 \ No newline at end of file diff --git a/tests/forms/TableListFieldTest.php b/tests/forms/TableListFieldTest.php deleted file mode 100644 index 161d73e99..000000000 --- a/tests/forms/TableListFieldTest.php +++ /dev/null @@ -1,375 +0,0 @@ - "Col A", - "B" => "Col B", - "C" => "Col C", - "D" => "Col D", - "E" => "Col E", - )); - // A TableListField must be inside a form for its links to be generated - $form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldList( - $table - ), new FieldList()); - - $result = $table->FieldHolder(); - - // Do a quick check to ensure that some of the D() and getE() values got through - $this->assertRegExp('/>\s*a2\s*assertRegExp('/>\s*a2\/b2\/c2\s*assertRegExp('/>\s*a2-eobjFromFixture('TableListFieldTest_Obj', 'one'); - $item2 = $this->objFromFixture('TableListFieldTest_Obj', 'two'); - $item3 = $this->objFromFixture('TableListFieldTest_Obj', 'three'); - $item4 = $this->objFromFixture('TableListFieldTest_Obj', 'four'); - $item5 = $this->objFromFixture('TableListFieldTest_Obj', 'five'); - - // In this simple case, the source items should just list all the data objects specified - $table = new TableListField("Tester", "TableListFieldTest_Obj", array( - "A" => "Col A", - "B" => "Col B", - "C" => "Col C", - "D" => "Col D", - "E" => "Col E", - )); - // A TableListField must be inside a form for its links to be generated - $form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldList( - $table - ), new FieldList()); - - $items = $table->sourceItems(); - $this->assertNotNull($items); - - $itemMap = $items->map("ID", "A") ; - $this->assertEquals(array( - $item1->ID => "a1", - $item2->ID => "a2", - $item3->ID => "a3", - $item4->ID => "a4", - $item5->ID => "a5" - ), $itemMap->toArray()); - } - - public function testFirstPageOfPaginatedSourceItemGeneration() { - $item1 = $this->objFromFixture('TableListFieldTest_Obj', 'one'); - $item2 = $this->objFromFixture('TableListFieldTest_Obj', 'two'); - $item3 = $this->objFromFixture('TableListFieldTest_Obj', 'three'); - $item4 = $this->objFromFixture('TableListFieldTest_Obj', 'four'); - $item5 = $this->objFromFixture('TableListFieldTest_Obj', 'five'); - - // With pagination enabled, only the first page of items should be shown - $table = new TableListField("Tester", "TableListFieldTest_Obj", array( - "A" => "Col A", - "B" => "Col B", - "C" => "Col C", - "D" => "Col D", - "E" => "Col E", - )); - // A TableListField must be inside a form for its links to be generated - $form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldList( - $table - ), new FieldList()); - - $table->ShowPagination = true; - $table->PageSize = 2; - - $items = $table->sourceItems(); - $this->assertNotNull($items); - - $itemMap = $items->map("ID", "A") ; - $this->assertEquals(array( - $item1->ID => "a1", - $item2->ID => "a2" - ), $itemMap->toArray()); - } - - public function testSecondPageOfPaginatedSourceItemGeneration() { - $item1 = $this->objFromFixture('TableListFieldTest_Obj', 'one'); - $item2 = $this->objFromFixture('TableListFieldTest_Obj', 'two'); - $item3 = $this->objFromFixture('TableListFieldTest_Obj', 'three'); - $item4 = $this->objFromFixture('TableListFieldTest_Obj', 'four'); - $item5 = $this->objFromFixture('TableListFieldTest_Obj', 'five'); - - // With pagination enabled, only the first page of items should be shown - $table = new TableListField("Tester", "TableListFieldTest_Obj", array( - "A" => "Col A", - "B" => "Col B", - "C" => "Col C", - "D" => "Col D", - "E" => "Col E", - )); - // A TableListField must be inside a form for its links to be generated - $form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldList( - $table - ), new FieldList()); - - $table->ShowPagination = true; - $table->PageSize = 2; - $_REQUEST['ctf']['Tester']['start'] = 2; - - $items = $table->sourceItems(); - $this->assertNotNull($items); - - $itemMap = $items->map("ID", "A") ; - $this->assertEquals(array($item3->ID => "a3", $item4->ID => "a4"), $itemMap->toArray()); - } - - public function testSelectOptionsAddRemove() { - $table = new TableListField("Tester", "TableListFieldTest_Obj", array( - "A" => "Col A", - )); - $this->assertNull($table->SelectOptions(), 'Empty by default'); - - $table->addSelectOptions(array("F"=>"FieldF", 'G'=>'FieldG')); - $this->assertEquals($table->SelectOptions()->map('Key', 'Value'), array("F"=>"FieldF",'G'=>'FieldG')); - - $table->removeSelectOptions(array("F")); - $this->assertEquals($table->SelectOptions()->map('Key', 'Value'), array("G"=>"FieldG")); - } - - public function testSelectOptionsRendering() { - $obj1 = $this->objFromFixture('TableListFieldTest_Obj', 'one'); - $obj2 = $this->objFromFixture('TableListFieldTest_Obj', 'two'); - $obj3 = $this->objFromFixture('TableListFieldTest_Obj', 'three'); - $table = new TableListField("Tester", "TableListFieldTest_Obj", array( - "A" => "Col A", - )); - $table->Markable = true; - - $table->addSelectOptions(array("F"=>"FieldF")); - $tableHTML = $table->FieldHolder(); - $p = new CSSContentParser($tableHTML); - $this->assertContains('rel="F"', $tableHTML); - $tbody = $p->getByXpath('//tbody'); - $this->assertContains('markingcheckbox F', (string)$tbody[0]->tr[0]->td[0]['class']); - $this->assertContains('markingcheckbox', (string)$tbody[0]->tr[1]->td[0]['class']); - $this->assertContains('markingcheckbox F', (string)$tbody[0]->tr[2]->td[0]['class']); - } - - /** - * Get that visiting the field's URL returns the content of the field. - * This capability is used by ajax - */ - public function testAjaxRefreshing() { - $controller = new TableListFieldTest_TestController(); - $table = $controller->TestForm()->Fields()->First(); - - $ajaxResponse = Director::test($table->Link())->getBody(); - - // Check that the column headings have been rendered - $this->assertRegExp('/]*>.*Col A.*<\/th>/si', $ajaxResponse); - $this->assertRegExp('/]*>.*Col B.*<\/th>/si', $ajaxResponse); - $this->assertRegExp('/]*>.*Col C.*<\/th>/si', $ajaxResponse); - $this->assertRegExp('/]*>.*Col D.*<\/th>/si', $ajaxResponse); - $this->assertRegExp('/]*>.*Col E.*<\/th>/si', $ajaxResponse); - } - - public function testCsvExport() { - $table = new TableListField("Tester", "TableListFieldTest_CsvExport", array( - "A" => "Col A", - "B" => "Col B" - )); - - $form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldList( - $table - ), new FieldList()); - - $csvResponse = $table->export(); - - $csvOutput = $csvResponse->getBody(); - - $this->assertNotEquals($csvOutput, false); - - // Create a temporary file and write the CSV to it. - $csvFileName = tempnam(TEMP_FOLDER, 'csv-export'); - $csvFile = fopen($csvFileName, 'wb'); - fwrite($csvFile, $csvOutput); - fclose($csvFile); - - $csvFile = fopen($csvFileName, 'rb'); - $csvRow = fgetcsv($csvFile); - $this->assertEquals( - $csvRow, - array('Col A', 'Col B') - ); - - // fgetcsv doesn't handle escaped quotes in the string in PHP 5.2, so we're asserting the - // raw string instead. - $this->assertEquals( - '"\"A field, with a comma\"","A second field"', - trim(fgets($csvFile)) - ); - - fclose($csvFile); - - unlink($csvFileName); - } - - public function testLink() { - // A TableListField must be inside a form for its links to be generated - $form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldList( - new TableListField("Tester", "TableListFieldTest_Obj", array( - "A" => "Col A", - "B" => "Col B", - "C" => "Col C", - "D" => "Col D", - "E" => "Col E", - )) - ), new FieldList()); - - $table = $form->Fields()->dataFieldByName('Tester'); - $this->assertEquals( - $table->Link('test'), - sprintf('TableListFieldTest_TestController/TestForm/field/Tester/test?SecurityID=%s', - $form->Fields()->dataFieldByName('SecurityID')->Value()) - ); - } - - public function testPreservedSortOptionsInPaginationLink() { - $item1 = $this->objFromFixture('TableListFieldTest_Obj', 'one'); - $item2 = $this->objFromFixture('TableListFieldTest_Obj', 'two'); - $item3 = $this->objFromFixture('TableListFieldTest_Obj', 'three'); - $item4 = $this->objFromFixture('TableListFieldTest_Obj', 'four'); - $item5 = $this->objFromFixture('TableListFieldTest_Obj', 'five'); - - /* With pagination enabled, only the first page of items should be shown */ - $table = new TableListField("Tester", "TableListFieldTest_Obj", array( - "A" => "Col A", - "B" => "Col B", - "C" => "Col C", - "D" => "Col D", - "E" => "Col E", - )); - // A TableListField must be inside a form for its links to be generated - $form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldList( - $table - ), new FieldList()); - - $table->ShowPagination = true; - $table->PageSize = 2; - - // first page & sort A column by ASC - $_REQUEST['ctf']['Tester']['start'] = 0; - $_REQUEST['ctf']['Tester']['sort'] = 'A'; - $this->assertContains('&ctf[Tester][sort]=A', $table->NextLink()); - $this->assertNotContains('ctf[Tester][dir]', $table->NextLink()); - $this->assertContains('&ctf[Tester][sort]=A', $table->LastLink()); - $this->assertNotContains('ctf[Tester][dir]', $table->LastLink()); - - // second page & sort A column by ASC - $_REQUEST['ctf']['Tester']['start'] = 2; - $this->assertContains('&ctf[Tester][sort]=A', $table->PrevLink()); - $this->assertNotContains('&ctf[Tester][dir]', $table->PrevLink()); - $this->assertContains('&ctf[Tester][sort]=A', $table->FirstLink()); - $this->assertNotContains('&ctf[Tester][dir]', $table->FirstLink()); - - // first page & sort A column by DESC - $_REQUEST['ctf']['Tester']['start'] = 0; - $_REQUEST['ctf']['Tester']['sort'] = 'A'; - $_REQUEST['ctf']['Tester']['dir'] = 'desc'; - $this->assertContains('&ctf[Tester][sort]=A', $table->NextLink()); - $this->assertContains('&ctf[Tester][dir]=desc', $table->NextLink()); - $this->assertContains('&ctf[Tester][sort]=A', $table->LastLink()); - $this->assertContains('&ctf[Tester][dir]=desc', $table->LastLink()); - - // second page & sort A column by DESC - $_REQUEST['ctf']['Tester']['start'] = 2; - $this->assertContains('&ctf[Tester][sort]=A', $table->PrevLink()); - $this->assertContains('&ctf[Tester][dir]=desc', $table->PrevLink()); - $this->assertContains('&ctf[Tester][sort]=A', $table->FirstLink()); - $this->assertContains('&ctf[Tester][dir]=desc', $table->FirstLink()); - - unset($_REQUEST['ctf']); - } - - /** - * Check that a SS_List can be passed to TableListField - */ - public function testDataObjectSet() { - $one = new TableListFieldTest_Obj; - $one->A = "A-one"; - $two = new TableListFieldTest_Obj; - $two->A = "A-two"; - $three = new TableListFieldTest_Obj; - $three->A = "A-three"; - - $list = new ArrayList(array($one, $two, $three)); - - // A TableListField must be inside a form for its links to be generated - $form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldList( - new TableListField("Tester", $list, array( - "A" => "Col A", - "B" => "Col B", - "C" => "Col C", - "D" => "Col D", - "E" => "Col E", - )) - ), new FieldList()); - - $table = $form->Fields()->dataFieldByName('Tester'); - $rendered = $table->FieldHolder(); - - $this->assertContains('A-one', $rendered); - $this->assertContains('A-two', $rendered); - $this->assertContains('A-three', $rendered); - } -} - -class TableListFieldTest_Obj extends DataObject implements TestOnly { - static $db = array( - "A" => "Varchar", - "B" => "Varchar", - "C" => "Varchar", - "F" => "Boolean", - ); - static $default_sort = "A"; - - public function D() { - return $this->A . '/' . $this->B . '/' . $this->C; - } - - public function getE() { - return $this->A . '-e'; - } -} - -class TableListFieldTest_CsvExport extends DataObject implements TestOnly { - static $db = array( - "A" => "Varchar", - "B" => "Varchar" - ); - static $default_sort = "A"; -} - -class TableListFieldTest_TestController extends Controller { - public function Link($action = null) { - return Controller::join_links("TableListFieldTest_TestController/", $action); - } - public function TestForm() { - $table = new TableListField("Table", "TableListFieldTest_Obj", array( - "A" => "Col A", - "B" => "Col B", - "C" => "Col C", - "D" => "Col D", - "E" => "Col E", - )); - $table->disableSorting(); - - // A TableListField must be inside a form for its links to be generated - return new Form($this, "TestForm", new FieldList( - $table - ), new FieldList()); - } -} diff --git a/tests/forms/TableListFieldTest.yml b/tests/forms/TableListFieldTest.yml deleted file mode 100644 index 25d72fa76..000000000 --- a/tests/forms/TableListFieldTest.yml +++ /dev/null @@ -1,31 +0,0 @@ -TableListFieldTest_Obj: - one: - A: a1 - B: b1 - C: c1 - F: true - two: - A: a2 - B: b2 - C: c2 - F: false - three: - A: a3 - B: b3 - C: c3 - F: true - four: - A: a4 - B: b4 - C: c4 - D: false - five: - A: a5 - B: b5 - C: c5 - F: true - -TableListFieldTest_CsvExport: - exportone: - A: "\"A field, with a comma\"" - B: A second field \ No newline at end of file diff --git a/thirdparty/scriptaculous/builder.js b/thirdparty/scriptaculous/builder.js deleted file mode 100644 index 5b15ba939..000000000 --- a/thirdparty/scriptaculous/builder.js +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) -// -// See scriptaculous.js for full license. - -var Builder = { - NODEMAP: { - AREA: 'map', - CAPTION: 'table', - COL: 'table', - COLGROUP: 'table', - LEGEND: 'fieldset', - OPTGROUP: 'select', - OPTION: 'select', - PARAM: 'object', - TBODY: 'table', - TD: 'table', - TFOOT: 'table', - TH: 'table', - THEAD: 'table', - TR: 'table' - }, - // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken, - // due to a Firefox bug - node: function(elementName) { - elementName = elementName.toUpperCase(); - - // try innerHTML approach - var parentTag = this.NODEMAP[elementName] || 'div'; - var parentElement = document.createElement(parentTag); - try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 - parentElement.innerHTML = "<" + elementName + ">"; - } catch(e) {} - var element = parentElement.firstChild || null; - - // see if browser added wrapping tags - if(element && (element.tagName != elementName)) - element = element.getElementsByTagName(elementName)[0]; - - // fallback to createElement approach - if(!element) element = document.createElement(elementName); - - // abort if nothing could be created - if(!element) return; - - // attributes (or text) - if(arguments[1]) - if(this._isStringOrNumber(arguments[1]) || - (arguments[1] instanceof Array)) { - this._children(element, arguments[1]); - } else { - var attrs = this._attributes(arguments[1]); - if(attrs.length) { - try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 - parentElement.innerHTML = "<" +elementName + " " + - attrs + ">"; - } catch(e) {} - element = parentElement.firstChild || null; - // workaround firefox 1.0.X bug - if(!element) { - element = document.createElement(elementName); - for(attr in arguments[1]) - element[attr == 'class' ? 'className' : attr] = arguments[1][attr]; - } - if(element.tagName != elementName) - element = parentElement.getElementsByTagName(elementName)[0]; - } - } - - // text, or array of children - if(arguments[2]) - this._children(element, arguments[2]); - - return element; - }, - _text: function(text) { - return document.createTextNode(text); - }, - _attributes: function(attributes) { - var attrs = []; - for(attribute in attributes) - attrs.push((attribute=='className' ? 'class' : attribute) + - '="' + attributes[attribute].toString().escapeHTML() + '"'); - return attrs.join(" "); - }, - _children: function(element, children) { - if(typeof children=='object') { // array can hold nodes and text - children.flatten().each( function(e) { - if(typeof e=='object') - element.appendChild(e) - else - if(Builder._isStringOrNumber(e)) - element.appendChild(Builder._text(e)); - }); - } else - if(Builder._isStringOrNumber(children)) - element.appendChild(Builder._text(children)); - }, - _isStringOrNumber: function(param) { - return(typeof param=='string' || typeof param=='number'); - } -} \ No newline at end of file diff --git a/thirdparty/scriptaculous/controls.js b/thirdparty/scriptaculous/controls.js deleted file mode 100644 index f16d19a6c..000000000 --- a/thirdparty/scriptaculous/controls.js +++ /dev/null @@ -1,750 +0,0 @@ -// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) -// (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan) -// (c) 2005 Jon Tirsen (http://www.tirsen.com) -// Contributors: -// Richard Livsey -// Rahul Bhargava -// Rob Wills -// -// See scriptaculous.js for full license. - -// Autocompleter.Base handles all the autocompletion functionality -// that's independent of the data source for autocompletion. This -// includes drawing the autocompletion menu, observing keyboard -// and mouse events, and similar. -// -// Specific autocompleters need to provide, at the very least, -// a getUpdatedChoices function that will be invoked every time -// the text inside the monitored textbox changes. This method -// should get the text for which to provide autocompletion by -// invoking this.getToken(), NOT by directly accessing -// this.element.value. This is to allow incremental tokenized -// autocompletion. Specific auto-completion logic (AJAX, etc) -// belongs in getUpdatedChoices. -// -// Tokenized incremental autocompletion is enabled automatically -// when an autocompleter is instantiated with the 'tokens' option -// in the options parameter, e.g.: -// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' }); -// will incrementally autocomplete with a comma as the token. -// Additionally, ',' in the above example can be replaced with -// a token array, e.g. { tokens: [',', '\n'] } which -// enables autocompletion on multiple tokens. This is most -// useful when one of the tokens is \n (a newline), as it -// allows smart autocompletion after linebreaks. - -var Autocompleter = {} -Autocompleter.Base = function() {}; -Autocompleter.Base.prototype = { - baseInitialize: function(element, update, options) { - this.element = $(element); - this.update = $(update); - this.hasFocus = false; - this.changed = false; - this.active = false; - this.index = 0; - this.entryCount = 0; - - if (this.setOptions) - this.setOptions(options); - else - this.options = options || {}; - - this.options.paramName = this.options.paramName || this.element.name; - this.options.tokens = this.options.tokens || []; - this.options.frequency = this.options.frequency || 0.4; - this.options.minChars = this.options.minChars || 1; - this.options.onShow = this.options.onShow || - function(element, update){ - if(!update.style.position || update.style.position=='absolute') { - update.style.position = 'absolute'; - Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight}); - } - Effect.Appear(update,{duration:0.15}); - }; - this.options.onHide = this.options.onHide || - function(element, update){ new Effect.Fade(update,{duration:0.15}) }; - - if (typeof(this.options.tokens) == 'string') - this.options.tokens = new Array(this.options.tokens); - - this.observer = null; - - this.element.setAttribute('autocomplete','off'); - - Element.hide(this.update); - - Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this)); - Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this)); - }, - - show: function() { - if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update); - if(!this.iefix && - (navigator.appVersion.indexOf('MSIE')>0) && - (navigator.userAgent.indexOf('Opera')<0) && - (Element.getStyle(this.update, 'position')=='absolute')) { - new Insertion.After(this.update, - ''); - this.iefix = $(this.update.id+'_iefix'); - } - if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50); - }, - - fixIEOverlapping: function() { - Position.clone(this.update, this.iefix); - this.iefix.style.zIndex = 1; - this.update.style.zIndex = 2; - Element.show(this.iefix); - }, - - hide: function() { - this.stopIndicator(); - if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update); - if(this.iefix) Element.hide(this.iefix); - }, - - startIndicator: function() { - if(this.options.indicator) Element.show(this.options.indicator); - }, - - stopIndicator: function() { - if(this.options.indicator) Element.hide(this.options.indicator); - }, - - onKeyPress: function(event) { - if(this.active) - switch(event.keyCode) { - case Event.KEY_TAB: - case Event.KEY_RETURN: - this.selectEntry(); - Event.stop(event); - case Event.KEY_ESC: - this.hide(); - this.active = false; - Event.stop(event); - return; - case Event.KEY_LEFT: - case Event.KEY_RIGHT: - return; - case Event.KEY_UP: - this.markPrevious(); - this.render(); - if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event); - return; - case Event.KEY_DOWN: - this.markNext(); - this.render(); - if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event); - return; - } - else - if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN) - return; - - this.changed = true; - this.hasFocus = true; - - if(this.observer) clearTimeout(this.observer); - this.observer = - setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); - }, - - onHover: function(event) { - var element = Event.findElement(event, 'LI'); - if(this.index != element.autocompleteIndex) - { - this.index = element.autocompleteIndex; - this.render(); - } - Event.stop(event); - }, - - onClick: function(event) { - var element = Event.findElement(event, 'LI'); - this.index = element.autocompleteIndex; - this.selectEntry(); - this.hide(); - }, - - onBlur: function(event) { - // needed to make click events working - setTimeout(this.hide.bind(this), 250); - this.hasFocus = false; - this.active = false; - }, - - render: function() { - if(this.entryCount > 0) { - for (var i = 0; i < this.entryCount; i++) - this.index==i ? - Element.addClassName(this.getEntry(i),"selected") : - Element.removeClassName(this.getEntry(i),"selected"); - - if(this.hasFocus) { - this.show(); - this.active = true; - } - } else { - this.active = false; - this.hide(); - } - }, - - markPrevious: function() { - if(this.index > 0) this.index-- - else this.index = this.entryCount-1; - }, - - markNext: function() { - if(this.index < this.entryCount-1) this.index++ - else this.index = 0; - }, - - getEntry: function(index) { - return this.update.firstChild.childNodes[index]; - }, - - getCurrentEntry: function() { - return this.getEntry(this.index); - }, - - selectEntry: function() { - this.active = false; - this.updateElement(this.getCurrentEntry()); - }, - - updateElement: function(selectedElement) { - if (this.options.updateElement) { - this.options.updateElement(selectedElement); - return; - } - - var value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); - var lastTokenPos = this.findLastToken(); - if (lastTokenPos != -1) { - var newValue = this.element.value.substr(0, lastTokenPos + 1); - var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/); - if (whitespace) - newValue += whitespace[0]; - this.element.value = newValue + value; - } else { - this.element.value = value; - } - this.element.focus(); - - if (this.options.afterUpdateElement) - this.options.afterUpdateElement(this.element, selectedElement); - }, - - updateChoices: function(choices) { - if(!this.changed && this.hasFocus) { - this.update.innerHTML = choices; - Element.cleanWhitespace(this.update); - Element.cleanWhitespace(this.update.firstChild); - - if(this.update.firstChild && this.update.firstChild.childNodes) { - this.entryCount = - this.update.firstChild.childNodes.length; - for (var i = 0; i < this.entryCount; i++) { - var entry = this.getEntry(i); - entry.autocompleteIndex = i; - this.addObservers(entry); - } - } else { - this.entryCount = 0; - } - - this.stopIndicator(); - - this.index = 0; - this.render(); - } - }, - - addObservers: function(element) { - Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this)); - Event.observe(element, "click", this.onClick.bindAsEventListener(this)); - }, - - onObserverEvent: function() { - this.changed = false; - if(this.getToken().length>=this.options.minChars) { - this.startIndicator(); - this.getUpdatedChoices(); - } else { - this.active = false; - this.hide(); - } - }, - - getToken: function() { - var tokenPos = this.findLastToken(); - if (tokenPos != -1) - var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,''); - else - var ret = this.element.value; - - return /\n/.test(ret) ? '' : ret; - }, - - findLastToken: function() { - var lastTokenPos = -1; - - for (var i=0; i lastTokenPos) - lastTokenPos = thisTokenPos; - } - return lastTokenPos; - } -} - -Ajax.Autocompleter = Class.create(); -Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), { - initialize: function(element, update, url, options) { - this.baseInitialize(element, update, options); - this.options.asynchronous = true; - this.options.onComplete = this.onComplete.bind(this); - this.options.defaultParams = this.options.parameters || null; - this.url = url; - }, - - getUpdatedChoices: function() { - entry = encodeURIComponent(this.options.paramName) + '=' + - encodeURIComponent(this.getToken()); - - this.options.parameters = this.options.callback ? - this.options.callback(this.element, entry) : entry; - - if(this.options.defaultParams) - this.options.parameters += '&' + this.options.defaultParams; - - new Ajax.Request(this.url, this.options); - }, - - onComplete: function(request) { - this.updateChoices(request.responseText); - } - -}); - -// The local array autocompleter. Used when you'd prefer to -// inject an array of autocompletion options into the page, rather -// than sending out Ajax queries, which can be quite slow sometimes. -// -// The constructor takes four parameters. The first two are, as usual, -// the id of the monitored textbox, and id of the autocompletion menu. -// The third is the array you want to autocomplete from, and the fourth -// is the options block. -// -// Extra local autocompletion options: -// - choices - How many autocompletion choices to offer -// -// - partialSearch - If false, the autocompleter will match entered -// text only at the beginning of strings in the -// autocomplete array. Defaults to true, which will -// match text at the beginning of any *word* in the -// strings in the autocomplete array. If you want to -// search anywhere in the string, additionally set -// the option fullSearch to true (default: off). -// -// - fullSsearch - Search anywhere in autocomplete array strings. -// -// - partialChars - How many characters to enter before triggering -// a partial match (unlike minChars, which defines -// how many characters are required to do any match -// at all). Defaults to 2. -// -// - ignoreCase - Whether to ignore case when autocompleting. -// Defaults to true. -// -// It's possible to pass in a custom function as the 'selector' -// option, if you prefer to write your own autocompletion logic. -// In that case, the other options above will not apply unless -// you support them. - -Autocompleter.Local = Class.create(); -Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), { - initialize: function(element, update, array, options) { - this.baseInitialize(element, update, options); - this.options.array = array; - }, - - getUpdatedChoices: function() { - this.updateChoices(this.options.selector(this)); - }, - - setOptions: function(options) { - this.options = Object.extend({ - choices: 10, - partialSearch: true, - partialChars: 2, - ignoreCase: true, - fullSearch: false, - selector: function(instance) { - var ret = []; // Beginning matches - var partial = []; // Inside matches - var entry = instance.getToken(); - var count = 0; - - for (var i = 0; i < instance.options.array.length && - ret.length < instance.options.choices ; i++) { - - var elem = instance.options.array[i]; - var foundPos = instance.options.ignoreCase ? - elem.toLowerCase().indexOf(entry.toLowerCase()) : - elem.indexOf(entry); - - while (foundPos != -1) { - if (foundPos == 0 && elem.length != entry.length) { - ret.push("
  • " + elem.substr(0, entry.length) + "" + - elem.substr(entry.length) + "
  • "); - break; - } else if (entry.length >= instance.options.partialChars && - instance.options.partialSearch && foundPos != -1) { - if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) { - partial.push("
  • " + elem.substr(0, foundPos) + "" + - elem.substr(foundPos, entry.length) + "" + elem.substr( - foundPos + entry.length) + "
  • "); - break; - } - } - - foundPos = instance.options.ignoreCase ? - elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : - elem.indexOf(entry, foundPos + 1); - - } - } - if (partial.length) - ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)) - return "
      " + ret.join('') + "
    "; - } - }, options || {}); - } -}); - -// AJAX in-place editor -// -// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor - -// Use this if you notice weird scrolling problems on some browsers, -// the DOM might be a bit confused when this gets called so do this -// waits 1 ms (with setTimeout) until it does the activation -Field.scrollFreeActivate = function(field) { - setTimeout(function() { - Field.activate(field); - }, 1); -} - -Ajax.InPlaceEditor = Class.create(); -Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99"; -Ajax.InPlaceEditor.prototype = { - initialize: function(element, url, options) { - this.url = url; - this.element = $(element); - - this.options = Object.extend({ - okText: "ok", - cancelText: "cancel", - savingText: "Saving...", - clickToEditText: "Click to edit", - okText: "ok", - rows: 1, - onComplete: function(transport, element) { - new Effect.Highlight(element, {startcolor: this.options.highlightcolor}); - }, - onFailure: function(transport) { - alert("Error communicating with the server: " + transport.responseText.stripTags()); - }, - callback: function(form) { - return Form.serialize(form); - }, - handleLineBreaks: true, - loadingText: 'Loading...', - savingClassName: 'inplaceeditor-saving', - loadingClassName: 'inplaceeditor-loading', - formClassName: 'inplaceeditor-form', - highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor, - highlightendcolor: "#FFFFFF", - externalControl: null, - ajaxOptions: {} - }, options || {}); - - if(!this.options.formId && this.element.id) { - this.options.formId = this.element.id + "-inplaceeditor"; - if ($(this.options.formId)) { - // there's already a form with that name, don't specify an id - this.options.formId = null; - } - } - - if (this.options.externalControl) { - this.options.externalControl = $(this.options.externalControl); - } - - this.originalBackground = Element.getStyle(this.element, 'background-color'); - if (!this.originalBackground) { - this.originalBackground = "transparent"; - } - - this.element.title = this.options.clickToEditText; - - this.onclickListener = this.enterEditMode.bindAsEventListener(this); - this.mouseoverListener = this.enterHover.bindAsEventListener(this); - this.mouseoutListener = this.leaveHover.bindAsEventListener(this); - Event.observe(this.element, 'click', this.onclickListener); - Event.observe(this.element, 'mouseover', this.mouseoverListener); - Event.observe(this.element, 'mouseout', this.mouseoutListener); - if (this.options.externalControl) { - Event.observe(this.options.externalControl, 'click', this.onclickListener); - Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener); - Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener); - } - }, - enterEditMode: function(evt) { - if (this.saving) return; - if (this.editing) return; - this.editing = true; - this.onEnterEditMode(); - if (this.options.externalControl) { - Element.hide(this.options.externalControl); - } - Element.hide(this.element); - this.createForm(); - this.element.parentNode.insertBefore(this.form, this.element); - Field.scrollFreeActivate(this.editField); - // stop the event to avoid a page refresh in Safari - if (evt) { - Event.stop(evt); - } - return false; - }, - createForm: function() { - this.form = document.createElement("form"); - this.form.id = this.options.formId; - Element.addClassName(this.form, this.options.formClassName) - this.form.onsubmit = this.onSubmit.bind(this); - - this.createEditField(); - - if (this.options.textarea) { - var br = document.createElement("br"); - this.form.appendChild(br); - } - - okButton = document.createElement("input"); - okButton.type = "submit"; - okButton.value = this.options.okText; - this.form.appendChild(okButton); - - cancelLink = document.createElement("a"); - cancelLink.href = "#"; - cancelLink.appendChild(document.createTextNode(this.options.cancelText)); - cancelLink.onclick = this.onclickCancel.bind(this); - this.form.appendChild(cancelLink); - }, - hasHTMLLineBreaks: function(string) { - if (!this.options.handleLineBreaks) return false; - return string.match(/
    /i); - }, - convertHTMLLineBreaks: function(string) { - return string.replace(/
    /gi, "\n").replace(//gi, "\n").replace(/<\/p>/gi, "\n").replace(/

    /gi, ""); - }, - createEditField: function() { - var text; - if(this.options.loadTextURL) { - text = this.options.loadingText; - } else { - text = this.getText(); - } - - if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) { - this.options.textarea = false; - var textField = document.createElement("input"); - textField.type = "text"; - textField.name = "value"; - textField.value = text; - textField.style.backgroundColor = this.options.highlightcolor; - var size = this.options.size || this.options.cols || 0; - if (size != 0) textField.size = size; - this.editField = textField; - } else { - this.options.textarea = true; - var textArea = document.createElement("textarea"); - textArea.name = "value"; - textArea.value = this.convertHTMLLineBreaks(text); - textArea.rows = this.options.rows; - textArea.cols = this.options.cols || 40; - this.editField = textArea; - } - - if(this.options.loadTextURL) { - this.loadExternalText(); - } - this.form.appendChild(this.editField); - }, - getText: function() { - return this.element.innerHTML; - }, - loadExternalText: function() { - Element.addClassName(this.form, this.options.loadingClassName); - this.editField.disabled = true; - new Ajax.Request( - this.options.loadTextURL, - Object.extend({ - asynchronous: true, - onComplete: this.onLoadedExternalText.bind(this) - }, this.options.ajaxOptions) - ); - }, - onLoadedExternalText: function(transport) { - Element.removeClassName(this.form, this.options.loadingClassName); - this.editField.disabled = false; - this.editField.value = transport.responseText.stripTags(); - }, - onclickCancel: function() { - this.onComplete(); - this.leaveEditMode(); - return false; - }, - onFailure: function(transport) { - this.options.onFailure(transport); - if (this.oldInnerHTML) { - this.element.innerHTML = this.oldInnerHTML; - this.oldInnerHTML = null; - } - return false; - }, - onSubmit: function() { - // onLoading resets these so we need to save them away for the Ajax call - var form = this.form; - var value = this.editField.value; - - // do this first, sometimes the ajax call returns before we get a chance to switch on Saving... - // which means this will actually switch on Saving... *after* we've left edit mode causing Saving... - // to be displayed indefinitely - this.onLoading(); - - new Ajax.Updater( - { - success: this.element, - // don't update on failure (this could be an option) - failure: null - }, - this.url, - Object.extend({ - parameters: this.options.callback(form, value), - onComplete: this.onComplete.bind(this), - onFailure: this.onFailure.bind(this) - }, this.options.ajaxOptions) - ); - // stop the event to avoid a page refresh in Safari - if (arguments.length > 1) { - Event.stop(arguments[0]); - } - return false; - }, - onLoading: function() { - this.saving = true; - this.removeForm(); - this.leaveHover(); - this.showSaving(); - }, - showSaving: function() { - this.oldInnerHTML = this.element.innerHTML; - this.element.innerHTML = this.options.savingText; - Element.addClassName(this.element, this.options.savingClassName); - this.element.style.backgroundColor = this.originalBackground; - Element.show(this.element); - }, - removeForm: function() { - if(this.form) { - if (this.form.parentNode) Element.remove(this.form); - this.form = null; - } - }, - enterHover: function() { - if (this.saving) return; - this.element.style.backgroundColor = this.options.highlightcolor; - if (this.effect) { - this.effect.cancel(); - } - Element.addClassName(this.element, this.options.hoverClassName) - }, - leaveHover: function() { - if (this.options.backgroundColor) { - this.element.style.backgroundColor = this.oldBackground; - } - Element.removeClassName(this.element, this.options.hoverClassName) - if (this.saving) return; - this.effect = new Effect.Highlight(this.element, { - startcolor: this.options.highlightcolor, - endcolor: this.options.highlightendcolor, - restorecolor: this.originalBackground - }); - }, - leaveEditMode: function() { - Element.removeClassName(this.element, this.options.savingClassName); - this.removeForm(); - this.leaveHover(); - this.element.style.backgroundColor = this.originalBackground; - Element.show(this.element); - if (this.options.externalControl) { - Element.show(this.options.externalControl); - } - this.editing = false; - this.saving = false; - this.oldInnerHTML = null; - this.onLeaveEditMode(); - }, - onComplete: function(transport) { - this.leaveEditMode(); - this.options.onComplete.bind(this)(transport, this.element); - }, - onEnterEditMode: function() {}, - onLeaveEditMode: function() {}, - dispose: function() { - if (this.oldInnerHTML) { - this.element.innerHTML = this.oldInnerHTML; - } - this.leaveEditMode(); - Event.stopObserving(this.element, 'click', this.onclickListener); - Event.stopObserving(this.element, 'mouseover', this.mouseoverListener); - Event.stopObserving(this.element, 'mouseout', this.mouseoutListener); - if (this.options.externalControl) { - Event.stopObserving(this.options.externalControl, 'click', this.onclickListener); - Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener); - Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener); - } - } -}; - -// Delayed observer, like Form.Element.Observer, -// but waits for delay after last key input -// Ideal for live-search fields - -Form.Element.DelayedObserver = Class.create(); -Form.Element.DelayedObserver.prototype = { - initialize: function(element, delay, callback) { - this.delay = delay || 0.5; - this.element = $(element); - this.callback = callback; - this.timer = null; - this.lastValue = $F(this.element); - Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this)); - }, - delayedListener: function(event) { - if(this.lastValue == $F(this.element)) return; - if(this.timer) clearTimeout(this.timer); - this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000); - this.lastValue = $F(this.element); - }, - onTimerEvent: function() { - this.timer = null; - this.callback(this.element, $F(this.element)); - } -}; \ No newline at end of file diff --git a/thirdparty/scriptaculous/dragdrop.js b/thirdparty/scriptaculous/dragdrop.js deleted file mode 100644 index 03cd21046..000000000 --- a/thirdparty/scriptaculous/dragdrop.js +++ /dev/null @@ -1,581 +0,0 @@ -// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) -// -// See scriptaculous.js for full license. - -/*--------------------------------------------------------------------------*/ - -var Droppables = { - drops: [], - - remove: function(element) { - this.drops = this.drops.reject(function(d) { return d.element==$(element) }); - }, - - add: function(element) { - element = $(element); - var options = Object.extend({ - greedy: true, - hoverclass: null - }, arguments[1] || {}); - - // cache containers - if(options.containment) { - options._containers = []; - var containment = options.containment; - if((typeof containment == 'object') && - (containment.constructor == Array)) { - containment.each( function(c) { options._containers.push($(c)) }); - } else { - options._containers.push($(containment)); - } - } - - if(options.accept) options.accept = [options.accept].flatten(); - - // Element.makePositioned(element); // fix IE - options.element = element; - - this.drops.push(options); - }, - - isContained: function(element, drop) { - var parentNode = element.parentNode; - return drop._containers.detect(function(c) { return parentNode == c }); - }, - - isAffected: function(pX, pY, element, drop) { - return ( - (drop.element!=element) && (drop.element.parentNode) && - ((!drop._containers) || - this.isContained(element, drop)) && - ((!drop.accept) || - (Element.classNames(element).detect( - function(v) { return drop.accept.include(v) } ) )) && - ((!drop.checkDroppableIsntContained) || !Element.contains(element, drop.element)) && - Position.within(drop.element, pX, pY) ); - }, - - deactivate: function(drop) { - if(drop.hoverclass) - Element.removeClassName(drop.element, drop.hoverclass); - this.last_active = null; - }, - - activate: function(drop) { - if(this.last_active) this.deactivate(this.last_active); - if(drop.hoverclass) - Element.addClassName(drop.element, drop.hoverclass); - this.last_active = drop; - }, - - show: function(event, element) { - if(!this.drops.length) return; - var pX = Event.pointerX(event); - var pY = Event.pointerY(event); - Position.prepare(); - - var i = this.drops.length-1; do { - var drop = this.drops[i]; - if(this.isAffected(pX, pY, element, drop)) { - if(drop.onHover) - drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); - if(drop.greedy) { - this.activate(drop); - return; - } - } - } while (i--); - - if(this.last_active) this.deactivate(this.last_active); - }, - - fire: function(event, element) { - if(!this.last_active) return; - Position.prepare(); - - if (this.isAffected(Event.pointerX(event), Event.pointerY(event), element, this.last_active)) - if (this.last_active.onDrop) - this.last_active.onDrop(element, this.last_active.element, event); - }, - - reset: function() { - if(this.last_active) - this.deactivate(this.last_active); - } -} - -var Draggables = { - observers: [], - addObserver: function(observer) { - this.observers.push(observer); - this._cacheObserverCallbacks(); - }, - removeObserver: function(element) { // element instead of observer fixes mem leaks - this.observers = this.observers.reject( function(o) { return o.element==element }); - this._cacheObserverCallbacks(); - }, - notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag' - if(this[eventName+'Count'] > 0) - this.observers.each( function(o) { - if(o[eventName]) o[eventName](eventName, draggable, event); - }); - }, - _cacheObserverCallbacks: function() { - ['onStart','onEnd','onDrag'].each( function(eventName) { - Draggables[eventName+'Count'] = Draggables.observers.select( - function(o) { return o[eventName]; } - ).length; - }); - } -} - -/*--------------------------------------------------------------------------*/ - -var Draggable = Class.create(); -Draggable.prototype = { - initialize: function(element) { - var options = Object.extend({ - handle: false, - starteffect: function(element) { - new Effect.Opacity(element, {duration:0.2, from:1.0, to:0.7}); - }, - reverteffect: function(element, top_offset, left_offset) { - var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02; - new Effect.MoveBy(element, -top_offset, -left_offset, {duration:dur}); - }, - endeffect: function(element) { - new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0}); - }, - zindex: 1000, - revert: false, - snap: false // false, or xy or [x,y] or function(x,y){ return [x,y] } - }, arguments[1] || {}); - - this.element = $(element); - if(options.handle && (typeof options.handle == 'string')) - this.handle = Element.childrenWithClassName(this.element, options.handle)[0]; - - if(!this.handle) this.handle = $(options.handle); - if(!this.handle) this.handle = this.element; - - this.offsetX = 0; - this.offsetY = 0; - this.originalLeft = this.currentLeft(); - this.originalTop = this.currentTop(); - this.originalX = this.element.offsetLeft; - this.originalY = this.element.offsetTop; - - this.options = options; - - this.active = false; - this.dragging = false; - - this.eventMouseDown = this.startDrag.bindAsEventListener(this); - this.eventMouseUp = this.endDrag.bindAsEventListener(this); - this.eventMouseMove = this.update.bindAsEventListener(this); - this.eventKeypress = this.keyPress.bindAsEventListener(this); - - this.registerEvents(); - }, - destroy: function() { - Event.stopObserving(this.handle, "mousedown", this.eventMouseDown); - this.unregisterEvents(); - }, - registerEvents: function() { - //if(!Draggable.eventsRegistered) { - //Draggable.eventsRegistered = true; - Event.observe(document, "mouseup", this.eventMouseUp); - Event.observe(document, "mousemove", this.eventMouseMove); - Event.observe(document, "keypress", this.eventKeypress); - Event.observe(this.handle, "mousedown", this.eventMouseDown); - //} - }, - unregisterEvents: function() { - //if(!Draggable.eventsRegistered) { - //Draggable.eventsRegistered = false; - //if(!this.active) return; - Event.stopObserving(document, "mouseup", this.eventMouseUp); - Event.stopObserving(document, "mousemove", this.eventMouseMove); - Event.stopObserving(document, "keypress", this.eventKeypress); - //} - }, - currentLeft: function() { - return parseInt(this.element.style.left || '0'); - }, - currentTop: function() { - return parseInt(this.element.style.top || '0') - }, - startDrag: function(event) { - if(Event.isLeftClick(event)) { - - // abort on form elements, fixes a Firefox issue - var src = Event.element(event); - if(src.tagName && ( - src.tagName=='INPUT' || - src.tagName=='SELECT' || - src.tagName=='BUTTON' || - src.tagName=='TEXTAREA')) return; - - // this.registerEvents(); - Element.makePositioned(this.element); // fix IE - - this.element.wasDragged = false; - - this.active = true; - var pointer = [Event.pointerX(event), Event.pointerY(event)]; - var offsets = Position.cumulativeOffset(this.element); - this.offsetX = (pointer[0] - offsets[0]); - this.offsetY = (pointer[1] - offsets[1]); - Event.stop(event); - } - }, - finishDrag: function(event, success) { - // this.unregisterEvents(); - - this.active = false; - this.dragging = false; - this.element.wasDragged = true; - - if(this.options.ghosting) { - Position.relativize(this.element); - Element.remove(this._clone); - this._clone = null; - } - - if(success) Droppables.fire(event, this.element); - Draggables.notify('onEnd', this, event); - - var revert = this.options.revert; - if(revert && typeof revert == 'function') revert = revert(this.element); - - if(revert && this.options.reverteffect) { - this.options.reverteffect(this.element, - this.currentTop()-this.originalTop, - this.currentLeft()-this.originalLeft); - } else { - this.originalLeft = this.currentLeft(); - this.originalTop = this.currentTop(); - } - - if(this.options.zindex) - this.element.style.zIndex = this.originalZ; - - if(this.options.endeffect) - this.options.endeffect(this.element); - - - Droppables.reset(); - }, - keyPress: function(event) { - if(this.active) { - if(event.keyCode==Event.KEY_ESC) { - Element.undoPositioned(this.element); - this.finishDrag(event, false); - Event.stop(event); - } - } - }, - endDrag: function(event) { - Element.undoPositioned(this.element); - if(this.active && this.dragging) { - this.finishDrag(event, true); - Event.stop(event); - } - this.active = false; - this.dragging = false; - }, - draw: function(event) { - var pointer = [Event.pointerX(event), Event.pointerY(event)]; - var offsets = Position.cumulativeOffset(this.element); - offsets[0] -= this.currentLeft(); - offsets[1] -= this.currentTop(); - var style = this.element.style; - - var pos = [ - (pointer[0] - offsets[0] - this.offsetX), - (pointer[1] - offsets[1] - this.offsetY)]; - - if(this.options.snap) { - if(typeof this.options.snap == 'function') { - pos = this.options.snap(pos[0],pos[1]); - } else { - var draggable = this; - if(this.options.snap instanceof Array) { - pos = pos.collect( function(v, i) { - return Math.round(v/draggable.options.snap[i])*draggable.options.snap[i] }) - } else { - pos = pos.collect( function(v) { - return Math.round(v/draggable.options.snap)*draggable.options.snap }) - } - }} - - if((!this.options.constraint) || (this.options.constraint=='horizontal')) - style.left = pos[0] + "px"; - if((!this.options.constraint) || (this.options.constraint=='vertical')) - style.top = pos[1] + "px"; - if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering - }, - update: function(event) { - if(this.active) { - if(!this.dragging) { - if(this.options.onStartDrag) this.options.onStartDrag(this.element); - - var style = this.element.style; - this.dragging = true; - - if(Element.getStyle(this.element,'position')=='') - style.position = "relative"; - - if(this.options.zindex) { - this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); - style.zIndex = this.options.zindex; - } - - if(this.options.ghosting) { - this._clone = this.element.cloneNode(true); - Position.absolutize(this.element); - this.element.parentNode.insertBefore(this._clone, this.element); - } - - Draggables.notify('onStart', this, event); - if(this.options.starteffect) this.options.starteffect(this.element); - } - - Droppables.show(event, this.element); - Draggables.notify('onDrag', this, event); - this.draw(event); - if(this.options.change) this.options.change(this); - - // fix AppleWebKit rendering - if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); - - Event.stop(event); - } - } -} - -function rollingStatus(chr, maxLength) { - var txt = window.status + chr; - if(txt.length > maxLength) txt = txt.substr(txt.length - maxLength); - window.status = txt; -} - -/*--------------------------------------------------------------------------*/ - -var SortableObserver = Class.create(); -SortableObserver.prototype = { - initialize: function(element, observer) { - this.element = $(element); - this.observer = observer; - this.lastValue = Sortable.serialize(this.element); - }, - onStart: function() { - this.lastValue = Sortable.serialize(this.element); - }, - onEnd: function() { - Sortable.unmark(); - if(this.lastValue != Sortable.serialize(this.element)) - this.observer(this.element) - } -} - -var Sortable = { - sortables: new Array(), - options: function(element){ - element = $(element); - return this.sortables.detect(function(s) { return s.element == element }); - }, - destroy: function(element){ - element = $(element); - this.sortables.findAll(function(s) { return s.element == element }).each(function(s){ - Draggables.removeObserver(s.element); - s.droppables.each(function(d){ Droppables.remove(d) }); - s.draggables.invoke('destroy'); - }); - this.sortables = this.sortables.reject(function(s) { return s.element == element }); - }, - create: function(element) { - - element = $(element); - var options = Object.extend({ - element: element, - tag: 'li', // assumes li children, override with tag: 'tagname' - dropOnEmpty: false, - tree: false, // fixme: unimplemented - overlap: 'vertical', // one of 'vertical', 'horizontal' - constraint: 'vertical', // one of 'vertical', 'horizontal', false - containment: element, // also takes array of elements (or id's); or false - handle: false, // or a CSS class - only: false, - hoverclass: null, - ghosting: false, - format: null, - onChange: Prototype.emptyFunction, - onUpdate: Prototype.emptyFunction - }, arguments[1] || {}); - - // clear any old sortable with same element - this.destroy(element); - - // build options for the draggables - var options_for_draggable = { - revert: true, - ghosting: options.ghosting, - constraint: options.constraint, - handle: options.handle }; - - if(options.starteffect) - options_for_draggable.starteffect = options.starteffect; - - if(options.reverteffect) - options_for_draggable.reverteffect = options.reverteffect; - else - if(options.ghosting) options_for_draggable.reverteffect = function(element) { - element.style.top = 0; - element.style.left = 0; - }; - - if(options.endeffect) - options_for_draggable.endeffect = options.endeffect; - - if(options.zindex) - options_for_draggable.zindex = options.zindex; - - // build options for the droppables - var options_for_droppable = { - overlap: options.overlap, - containment: options.containment, - hoverclass: options.hoverclass, - onHover: Sortable.onHover, - greedy: !options.dropOnEmpty - } - - // fix for gecko engine - Element.cleanWhitespace(element); - - options.draggables = []; - options.droppables = []; - - // make it so - - // drop on empty handling - if(options.dropOnEmpty) { - Droppables.add(element, - {containment: options.containment, onHover: Sortable.onEmptyHover, greedy: false}); - options.droppables.push(element); - } - - (this.findElements(element, options) || []).each( function(e) { - // handles are per-draggable - var handle = options.handle ? - Element.childrenWithClassName(e, options.handle)[0] : e; - options.draggables.push( - new Draggable(e, Object.extend(options_for_draggable, { handle: handle }))); - Droppables.add(e, options_for_droppable); - options.droppables.push(e); - }); - - // keep reference - this.sortables.push(options); - - // for onupdate - Draggables.addObserver(new SortableObserver(element, options.onUpdate)); - - }, - - // return all suitable-for-sortable elements in a guaranteed order - findElements: function(element, options) { - if(!element.hasChildNodes()) return null; - var elements = []; - $A(element.childNodes).each( function(e) { - if(e.tagName && e.tagName==options.tag.toUpperCase() && - (!options.only || (Element.hasClassName(e, options.only)))) - elements.push(e); - if(options.tree) { - var grandchildren = this.findElements(e, options); - if(grandchildren) elements.push(grandchildren); - } - }); - - return (elements.length>0 ? elements.flatten() : null); - }, - - onHover: function(element, dropon, overlap) { - if(overlap>0.5) { - Sortable.mark(dropon, 'before'); - if(dropon.previousSibling != element) { - var oldParentNode = element.parentNode; - element.style.visibility = "hidden"; // fix gecko rendering - dropon.parentNode.insertBefore(element, dropon); - if(dropon.parentNode!=oldParentNode) - Sortable.options(oldParentNode).onChange(element); - Sortable.options(dropon.parentNode).onChange(element); - } - } else { - Sortable.mark(dropon, 'after'); - var nextElement = dropon.nextSibling || null; - if(nextElement != element) { - var oldParentNode = element.parentNode; - element.style.visibility = "hidden"; // fix gecko rendering - dropon.parentNode.insertBefore(element, nextElement); - if(dropon.parentNode!=oldParentNode) - Sortable.options(oldParentNode).onChange(element); - Sortable.options(dropon.parentNode).onChange(element); - } - } - }, - - onEmptyHover: function(element, dropon) { - if(element.parentNode!=dropon) { - var oldParentNode = element.parentNode; - dropon.appendChild(element); - Sortable.options(oldParentNode).onChange(element); - Sortable.options(dropon).onChange(element); - } - }, - - unmark: function() { - if(Sortable._marker) Element.hide(Sortable._marker); - }, - - mark: function(dropon, position) { - // mark on ghosting only - var sortable = Sortable.options(dropon.parentNode); - if(sortable && !sortable.ghosting) return; - - if(!Sortable._marker) { - Sortable._marker = $('dropmarker') || document.createElement('DIV'); - Element.hide(Sortable._marker); - Element.addClassName(Sortable._marker, 'dropmarker'); - Sortable._marker.style.position = 'absolute'; - document.getElementsByTagName("body").item(0).appendChild(Sortable._marker); - } - var offsets = Position.cumulativeOffset(dropon); - Sortable._marker.style.left = offsets[0] + 'px'; - Sortable._marker.style.top = offsets[1] + 'px'; - - if(position=='after') - if(sortable.overlap == 'horizontal') - Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px'; - else - Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px'; - - Element.show(Sortable._marker); - }, - - serialize: function(element) { - element = $(element); - var sortableOptions = this.options(element); - var options = Object.extend({ - tag: sortableOptions.tag, - only: sortableOptions.only, - name: element.id, - format: sortableOptions.format || /^[^_]*_(.*)$/ - }, arguments[1] || {}); - var ret = $(this.findElements(element, options) || []).collect( function(item) { - return (encodeURIComponent(options.name) + "[]=" + - encodeURIComponent(item.id.match(options.format) ? item.id.match(options.format)[1] : '')); - }).join("&"); - return ret; - } -} \ No newline at end of file diff --git a/thirdparty/scriptaculous/effects.js b/thirdparty/scriptaculous/effects.js deleted file mode 100644 index 145b78b43..000000000 --- a/thirdparty/scriptaculous/effects.js +++ /dev/null @@ -1,904 +0,0 @@ -// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) -// Contributors: -// Justin Palmer (http://encytemedia.com/) -// Mark Pilgrim (http://diveintomark.org/) -// Martin Bialasinki -// -// See scriptaculous.js for full license. - -/* ------------- element ext -------------- */ - -// converts rgb() and #xxx to #xxxxxx format, -// returns self (or first argument) if not convertable -String.prototype.parseColor = function() { - color = "#"; - if(this.slice(0,4) == "rgb(") { - var cols = this.slice(4,this.length-1).split(','); - var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); - } else { - if(this.slice(0,1) == '#') { - if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); - if(this.length==7) color = this.toLowerCase(); - } - } - return(color.length==7 ? color : (arguments[0] || this)); -} - -Element.collectTextNodesIgnoreClass = function(element, ignoreclass) { - var children = $(element).childNodes; - var text = ""; - var classtest = new RegExp("^([^ ]+ )*" + ignoreclass+ "( [^ ]+)*$","i"); - - for (var i = 0; i < children.length; i++) { - if(children[i].nodeType==3) { - text+=children[i].nodeValue; - } else { - if((!children[i].className.match(classtest)) && children[i].hasChildNodes()) - text += Element.collectTextNodesIgnoreClass(children[i], ignoreclass); - } - } - - return text; -} - -Element.setContentZoom = function(element, percent) { - element = $(element); - element.style.fontSize = (percent/100) + "em"; - if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); -} - -Element.getOpacity = function(element){ - var opacity; - if (opacity = Element.getStyle(element, "opacity")) - return parseFloat(opacity); - if (opacity = (Element.getStyle(element, "filter") || '').match(/alpha\(opacity=(.*)\)/)) - if(opacity[1]) return parseFloat(opacity[1]) / 100; - return 1.0; -} - -Element.setOpacity = function(element, value){ - element= $(element); - var els = element.style; - if (value == 1){ - els.opacity = '0.999999'; - if(/MSIE/.test(navigator.userAgent)) - els.filter = Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,''); - } else { - if(value < 0.00001) value = 0; - els.opacity = value; - if(/MSIE/.test(navigator.userAgent)) - els.filter = Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') + - "alpha(opacity="+value*100+")"; - } -} - -Element.getInlineOpacity = function(element){ - element= $(element); - var op; - op = element.style.opacity; - if (typeof op != "undefined" && op != "") return op; - return ""; -} - -Element.setInlineOpacity = function(element, value){ - element= $(element); - var els = element.style; - els.opacity = value; -} - -Element.childrenWithClassName = function(element, className) { - return $A($(element).getElementsByTagName('*')).select( - function(c) { return Element.hasClassName(c, className) }); -} - -/*--------------------------------------------------------------------------*/ - -var Effect = { - tagifyText: function(element) { - var tagifyStyle = "position:relative"; - if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ";zoom:1"; - element = $(element); - $A(element.childNodes).each( function(child) { - if(child.nodeType==3) { - child.nodeValue.toArray().each( function(character) { - element.insertBefore( - Builder.node('span',{style: tagifyStyle}, - character == " " ? String.fromCharCode(160) : character), - child); - }); - Element.remove(child); - } - }); - }, - multiple: function(element, effect) { - var elements; - if(((typeof element == 'object') || - (typeof element == 'function')) && - (element.length)) - elements = element; - else - elements = $(element).childNodes; - - var options = Object.extend({ - speed: 0.1, - delay: 0.0 - }, arguments[2] || {}); - var speed = options.speed; - var delay = options.delay; - - $A(elements).each( function(element, index) { - new effect(element, Object.extend(options, { delay: delay + index * speed })); - }); - } -}; - -var Effect2 = Effect; // deprecated - -/* ------------- transitions ------------- */ - -Effect.Transitions = {} - -Effect.Transitions.linear = function(pos) { - return pos; -} -Effect.Transitions.sinoidal = function(pos) { - return (-Math.cos(pos*Math.PI)/2) + 0.5; -} -Effect.Transitions.reverse = function(pos) { - return 1-pos; -} -Effect.Transitions.flicker = function(pos) { - return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; -} -Effect.Transitions.wobble = function(pos) { - return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; -} -Effect.Transitions.pulse = function(pos) { - return (Math.floor(pos*10) % 2 == 0 ? - (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10))); -} -Effect.Transitions.none = function(pos) { - return 0; -} -Effect.Transitions.full = function(pos) { - return 1; -} - -/* ------------- core effects ------------- */ - -Effect.Queue = { - effects: [], - _each: function(iterator) { - this.effects._each(iterator); - }, - interval: null, - add: function(effect) { - var timestamp = new Date().getTime(); - - switch(effect.options.queue) { - case 'front': - // move unstarted effects after this effect - this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { - e.startOn += effect.finishOn; - e.finishOn += effect.finishOn; - }); - break; - case 'end': - // start effect after last queued effect has finished - timestamp = this.effects.pluck('finishOn').max() || timestamp; - break; - } - - effect.startOn += timestamp; - effect.finishOn += timestamp; - this.effects.push(effect); - if(!this.interval) - this.interval = setInterval(this.loop.bind(this), 40); - }, - remove: function(effect) { - this.effects = this.effects.reject(function(e) { return e==effect }); - if(this.effects.length == 0) { - clearInterval(this.interval); - this.interval = null; - } - }, - loop: function() { - var timePos = new Date().getTime(); - this.effects.invoke('loop', timePos); - } -} -Object.extend(Effect.Queue, Enumerable); - -Effect.Base = function() {}; -Effect.Base.prototype = { - position: null, - setOptions: function(options) { - this.options = Object.extend({ - transition: Effect.Transitions.sinoidal, - duration: 1.0, // seconds - fps: 25.0, // max. 25fps due to Effect.Queue implementation - sync: false, // true for combining - from: 0.0, - to: 1.0, - delay: 0.0, - queue: 'parallel' - }, options || {}); - }, - start: function(options) { - this.setOptions(options || {}); - this.currentFrame = 0; - this.state = 'idle'; - this.startOn = this.options.delay*1000; - this.finishOn = this.startOn + (this.options.duration*1000); - this.event('beforeStart'); - if(!this.options.sync) Effect.Queue.add(this); - }, - loop: function(timePos) { - if(timePos >= this.startOn) { - if(timePos >= this.finishOn) { - this.render(1.0); - this.cancel(); - this.event('beforeFinish'); - if(this.finish) this.finish(); - this.event('afterFinish'); - return; - } - var pos = (timePos - this.startOn) / (this.finishOn - this.startOn); - var frame = Math.round(pos * this.options.fps * this.options.duration); - if(frame > this.currentFrame) { - this.render(pos); - this.currentFrame = frame; - } - } - }, - render: function(pos) { - if(this.state == 'idle') { - this.state = 'running'; - this.event('beforeSetup'); - if(this.setup) this.setup(); - this.event('afterSetup'); - } - if(this.options.transition) pos = this.options.transition(pos); - pos *= (this.options.to-this.options.from); - pos += this.options.from; - this.position = pos; - this.event('beforeUpdate'); - if(this.update) this.update(pos); - this.event('afterUpdate'); - }, - cancel: function() { - if(!this.options.sync) Effect.Queue.remove(this); - this.state = 'finished'; - }, - event: function(eventName) { - if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); - if(this.options[eventName]) this.options[eventName](this); - } -} - -Effect.Parallel = Class.create(); -Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), { - initialize: function(effects) { - this.effects = effects || []; - this.start(arguments[1]); - }, - update: function(position) { - this.effects.invoke('render', position); - }, - finish: function(position) { - this.effects.each( function(effect) { - effect.render(1.0); - effect.cancel(); - effect.event('beforeFinish'); - if(effect.finish) effect.finish(position); - effect.event('afterFinish'); - }); - } -}); - -Effect.Opacity = Class.create(); -Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), { - initialize: function(element) { - this.element = $(element); - // make this work on IE on elements without 'layout' - if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout)) - this.element.style.zoom = 1; - var options = Object.extend({ - from: Element.getOpacity(this.element) || 0.0, - to: 1.0 - }, arguments[1] || {}); - this.start(options); - }, - update: function(position) { - Element.setOpacity(this.element, position); - } -}); - -Effect.MoveBy = Class.create(); -Object.extend(Object.extend(Effect.MoveBy.prototype, Effect.Base.prototype), { - initialize: function(element, toTop, toLeft) { - this.element = $(element); - this.toTop = toTop; - this.toLeft = toLeft; - this.start(arguments[3]); - }, - setup: function() { - // Bug in Opera: Opera returns the "real" position of a static element or - // relative element that does not have top/left explicitly set. - // ==> Always set top and left for position relative elements in your stylesheets - // (to 0 if you do not need them) - - Element.makePositioned(this.element); - this.originalTop = parseFloat(Element.getStyle(this.element,'top') || '0'); - this.originalLeft = parseFloat(Element.getStyle(this.element,'left') || '0'); - }, - update: function(position) { - var topd = this.toTop * position + this.originalTop; - var leftd = this.toLeft * position + this.originalLeft; - this.setPosition(topd, leftd); - }, - setPosition: function(topd, leftd) { - this.element.style.top = topd + "px"; - this.element.style.left = leftd + "px"; - } -}); - -Effect.Scale = Class.create(); -Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { - initialize: function(element, percent) { - this.element = $(element) - var options = Object.extend({ - scaleX: true, - scaleY: true, - scaleContent: true, - scaleFromCenter: false, - scaleMode: 'box', // 'box' or 'contents' or {} with provided values - scaleFrom: 100.0, - scaleTo: percent - }, arguments[2] || {}); - this.start(options); - }, - setup: function() { - var effect = this; - - this.restoreAfterFinish = this.options.restoreAfterFinish || false; - this.elementPositioning = Element.getStyle(this.element,'position'); - - effect.originalStyle = {}; - ['top','left','width','height','fontSize'].each( function(k) { - effect.originalStyle[k] = effect.element.style[k]; - }); - - this.originalTop = this.element.offsetTop; - this.originalLeft = this.element.offsetLeft; - - var fontSize = Element.getStyle(this.element,'font-size') || "100%"; - ['em','px','%'].each( function(fontSizeType) { - if(fontSize.indexOf(fontSizeType)>0) { - effect.fontSize = parseFloat(fontSize); - effect.fontSizeType = fontSizeType; - } - }); - - this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; - - this.dims = null; - if(this.options.scaleMode=='box') - this.dims = [this.element.clientHeight, this.element.clientWidth]; - if(/^content/.test(this.options.scaleMode)) - this.dims = [this.element.scrollHeight, this.element.scrollWidth]; - if(!this.dims) - this.dims = [this.options.scaleMode.originalHeight, - this.options.scaleMode.originalWidth]; - }, - update: function(position) { - var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); - if(this.options.scaleContent && this.fontSize) - this.element.style.fontSize = this.fontSize*currentScale + this.fontSizeType; - this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); - }, - finish: function(position) { - if (this.restoreAfterFinish) { - var effect = this; - ['top','left','width','height','fontSize'].each( function(k) { - effect.element.style[k] = effect.originalStyle[k]; - }); - } - }, - setDimensions: function(height, width) { - var els = this.element.style; - if(this.options.scaleX) els.width = width + 'px'; - if(this.options.scaleY) els.height = height + 'px'; - if(this.options.scaleFromCenter) { - var topd = (height - this.dims[0])/2; - var leftd = (width - this.dims[1])/2; - if(this.elementPositioning == 'absolute') { - if(this.options.scaleY) els.top = this.originalTop-topd + "px"; - if(this.options.scaleX) els.left = this.originalLeft-leftd + "px"; - } else { - if(this.options.scaleY) els.top = -topd + "px"; - if(this.options.scaleX) els.left = -leftd + "px"; - } - } - } -}); - -Effect.Highlight = Class.create(); -Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), { - initialize: function(element) { - this.element = $(element); - var options = Object.extend({ - startcolor: "#ffff99" - }, arguments[1] || {}); - this.start(options); - }, - setup: function() { - // Prevent executing on elements not in the layout flow - if(this.element.style.display=='none') { this.cancel(); return; } - // Disable background image during the effect - this.oldBgImage = this.element.style.backgroundImage; - this.element.style.backgroundImage = "none"; - if(!this.options.endcolor) - this.options.endcolor = Element.getStyle(this.element, 'background-color').parseColor('#ffffff'); - if (typeof this.options.restorecolor == "undefined") - this.options.restorecolor = this.element.style.backgroundColor; - // init color calculations - this.colors_base = [ - parseInt(this.options.startcolor.slice(1,3),16), - parseInt(this.options.startcolor.slice(3,5),16), - parseInt(this.options.startcolor.slice(5),16) ]; - this.colors_delta = [ - parseInt(this.options.endcolor.slice(1,3),16)-this.colors_base[0], - parseInt(this.options.endcolor.slice(3,5),16)-this.colors_base[1], - parseInt(this.options.endcolor.slice(5),16)-this.colors_base[2]]; - }, - update: function(position) { - var effect = this; var colors = $R(0,2).map( function(i){ - return Math.round(effect.colors_base[i]+(effect.colors_delta[i]*position)) - }); - this.element.style.backgroundColor = "#" + - colors[0].toColorPart() + colors[1].toColorPart() + colors[2].toColorPart(); - }, - finish: function() { - this.element.style.backgroundColor = this.options.restorecolor; - this.element.style.backgroundImage = this.oldBgImage; - } -}); - -Effect.ScrollTo = Class.create(); -Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), { - initialize: function(element) { - this.element = $(element); - this.start(arguments[1] || {}); - }, - setup: function() { - Position.prepare(); - var offsets = Position.cumulativeOffset(this.element); - var max = window.innerHeight ? - window.height - window.innerHeight : - document.body.scrollHeight - - (document.documentElement.clientHeight ? - document.documentElement.clientHeight : document.body.clientHeight); - this.scrollStart = Position.deltaY; - this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart; - }, - update: function(position) { - Position.prepare(); - window.scrollTo(Position.deltaX, - this.scrollStart + (position*this.delta)); - } -}); - -/* ------------- combination effects ------------- */ - -Effect.Fade = function(element) { - var oldOpacity = Element.getInlineOpacity(element); - var options = Object.extend({ - from: Element.getOpacity(element) || 1.0, - to: 0.0, - afterFinishInternal: function(effect) - { if (effect.options.to == 0) { - Element.hide(effect.element); - Element.setInlineOpacity(effect.element, oldOpacity); - } - } - }, arguments[1] || {}); - return new Effect.Opacity(element,options); -} - -Effect.Appear = function(element) { - var options = Object.extend({ - from: (Element.getStyle(element, "display") == "none" ? 0.0 : Element.getOpacity(element) || 0.0), - to: 1.0, - beforeSetup: function(effect) - { Element.setOpacity(effect.element, effect.options.from); - Element.show(effect.element); } - }, arguments[1] || {}); - return new Effect.Opacity(element,options); -} - -Effect.Puff = function(element) { - element = $(element); - var oldOpacity = Element.getInlineOpacity(element); - var oldPosition = element.style.position; - return new Effect.Parallel( - [ new Effect.Scale(element, 200, - { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), - new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], - Object.extend({ duration: 1.0, - beforeSetupInternal: function(effect) - { effect.effects[0].element.style.position = 'absolute'; }, - afterFinishInternal: function(effect) - { Element.hide(effect.effects[0].element); - effect.effects[0].element.style.position = oldPosition; - Element.setInlineOpacity(effect.effects[0].element, oldOpacity); } - }, arguments[1] || {}) - ); -} - -Effect.BlindUp = function(element) { - element = $(element); - Element.makeClipping(element); - return new Effect.Scale(element, 0, - Object.extend({ scaleContent: false, - scaleX: false, - restoreAfterFinish: true, - afterFinishInternal: function(effect) - { - Element.hide(effect.element); - Element.undoClipping(effect.element); - } - }, arguments[1] || {}) - ); -} - -Effect.BlindDown = function(element) { - element = $(element); - var oldHeight = element.style.height; - var elementDimensions = Element.getDimensions(element); - return new Effect.Scale(element, 100, - Object.extend({ scaleContent: false, - scaleX: false, - scaleFrom: 0, - scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, - restoreAfterFinish: true, - afterSetup: function(effect) { - Element.makeClipping(effect.element); - effect.element.style.height = "0px"; - Element.show(effect.element); - }, - afterFinishInternal: function(effect) { - Element.undoClipping(effect.element); - effect.element.style.height = oldHeight; - } - }, arguments[1] || {}) - ); -} - -Effect.SwitchOff = function(element) { - element = $(element); - var oldOpacity = Element.getInlineOpacity(element); - return new Effect.Appear(element, { - duration: 0.4, - from: 0, - transition: Effect.Transitions.flicker, - afterFinishInternal: function(effect) { - new Effect.Scale(effect.element, 1, { - duration: 0.3, scaleFromCenter: true, - scaleX: false, scaleContent: false, restoreAfterFinish: true, - beforeSetup: function(effect) { - Element.makePositioned(effect.element); - Element.makeClipping(effect.element); - }, - afterFinishInternal: function(effect) { - Element.hide(effect.element); - Element.undoClipping(effect.element); - Element.undoPositioned(effect.element); - Element.setInlineOpacity(effect.element, oldOpacity); - } - }) - } - }); -} - -Effect.DropOut = function(element) { - element = $(element); - var oldTop = element.style.top; - var oldLeft = element.style.left; - var oldOpacity = Element.getInlineOpacity(element); - return new Effect.Parallel( - [ new Effect.MoveBy(element, 100, 0, { sync: true }), - new Effect.Opacity(element, { sync: true, to: 0.0 }) ], - Object.extend( - { duration: 0.5, - beforeSetup: function(effect) { - Element.makePositioned(effect.effects[0].element); }, - afterFinishInternal: function(effect) { - Element.hide(effect.effects[0].element); - Element.undoPositioned(effect.effects[0].element); - effect.effects[0].element.style.left = oldLeft; - effect.effects[0].element.style.top = oldTop; - Element.setInlineOpacity(effect.effects[0].element, oldOpacity); } - }, arguments[1] || {})); -} - -Effect.Shake = function(element) { - element = $(element); - var oldTop = element.style.top; - var oldLeft = element.style.left; - return new Effect.MoveBy(element, 0, 20, - { duration: 0.05, afterFinishInternal: function(effect) { - new Effect.MoveBy(effect.element, 0, -40, - { duration: 0.1, afterFinishInternal: function(effect) { - new Effect.MoveBy(effect.element, 0, 40, - { duration: 0.1, afterFinishInternal: function(effect) { - new Effect.MoveBy(effect.element, 0, -40, - { duration: 0.1, afterFinishInternal: function(effect) { - new Effect.MoveBy(effect.element, 0, 40, - { duration: 0.1, afterFinishInternal: function(effect) { - new Effect.MoveBy(effect.element, 0, -20, - { duration: 0.05, afterFinishInternal: function(effect) { - Element.undoPositioned(effect.element); - effect.element.style.left = oldLeft; - effect.element.style.top = oldTop; - }}) }}) }}) }}) }}) }}); -} - -Effect.SlideDown = function(element) { - element = $(element); - Element.cleanWhitespace(element); - // SlideDown need to have the content of the element wrapped in a container element with fixed height! - var oldInnerBottom = element.firstChild.style.bottom; - var elementDimensions = Element.getDimensions(element); - return new Effect.Scale(element, 100, - Object.extend({ scaleContent: false, - scaleX: false, - scaleFrom: 0, - scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, - restoreAfterFinish: true, - afterSetup: function(effect) { - Element.makePositioned(effect.element.firstChild); - if (window.opera) effect.element.firstChild.style.top = ""; - Element.makeClipping(effect.element); - element.style.height = '0'; - Element.show(element); - }, - afterUpdateInternal: function(effect) { - effect.element.firstChild.style.bottom = - (effect.dims[0] - effect.element.clientHeight) + 'px'; }, - afterFinishInternal: function(effect) { - Element.undoClipping(effect.element); - Element.undoPositioned(effect.element.firstChild); - effect.element.firstChild.style.bottom = oldInnerBottom; } - }, arguments[1] || {}) - ); -} - -Effect.SlideUp = function(element) { - element = $(element); - Element.cleanWhitespace(element); - var oldInnerBottom = element.firstChild.style.bottom; - return new Effect.Scale(element, 0, - Object.extend({ scaleContent: false, - scaleX: false, - scaleMode: 'box', - scaleFrom: 100, - restoreAfterFinish: true, - beforeStartInternal: function(effect) { - Element.makePositioned(effect.element.firstChild); - if (window.opera) effect.element.firstChild.style.top = ""; - Element.makeClipping(effect.element); - Element.show(element); - }, - afterUpdateInternal: function(effect) { - effect.element.firstChild.style.bottom = - (effect.dims[0] - effect.element.clientHeight) + 'px'; }, - afterFinishInternal: function(effect) { - Element.hide(effect.element); - Element.undoClipping(effect.element); - Element.undoPositioned(effect.element.firstChild); - effect.element.firstChild.style.bottom = oldInnerBottom; } - }, arguments[1] || {}) - ); -} - -Effect.Squish = function(element) { - // Bug in opera makes the TD containing this element expand for a instance after finish - return new Effect.Scale(element, window.opera ? 1 : 0, - { restoreAfterFinish: true, - beforeSetup: function(effect) { - Element.makeClipping(effect.element); }, - afterFinishInternal: function(effect) { - Element.hide(effect.element); - Element.undoClipping(effect.element); } - }); -} - -Effect.Grow = function(element) { - element = $(element); - var options = arguments[1] || {}; - - var elementDimensions = Element.getDimensions(element); - var originalWidth = elementDimensions.width; - var originalHeight = elementDimensions.height; - var oldTop = element.style.top; - var oldLeft = element.style.left; - var oldHeight = element.style.height; - var oldWidth = element.style.width; - var oldOpacity = Element.getInlineOpacity(element); - - var direction = options.direction || 'center'; - var moveTransition = options.moveTransition || Effect.Transitions.sinoidal; - var scaleTransition = options.scaleTransition || Effect.Transitions.sinoidal; - var opacityTransition = options.opacityTransition || Effect.Transitions.full; - - var initialMoveX, initialMoveY; - var moveX, moveY; - - switch (direction) { - case 'top-left': - initialMoveX = initialMoveY = moveX = moveY = 0; - break; - case 'top-right': - initialMoveX = originalWidth; - initialMoveY = moveY = 0; - moveX = -originalWidth; - break; - case 'bottom-left': - initialMoveX = moveX = 0; - initialMoveY = originalHeight; - moveY = -originalHeight; - break; - case 'bottom-right': - initialMoveX = originalWidth; - initialMoveY = originalHeight; - moveX = -originalWidth; - moveY = -originalHeight; - break; - case 'center': - initialMoveX = originalWidth / 2; - initialMoveY = originalHeight / 2; - moveX = -originalWidth / 2; - moveY = -originalHeight / 2; - break; - } - - return new Effect.MoveBy(element, initialMoveY, initialMoveX, { - duration: 0.01, - beforeSetup: function(effect) { - Element.hide(effect.element); - Element.makeClipping(effect.element); - Element.makePositioned(effect.element); - }, - afterFinishInternal: function(effect) { - new Effect.Parallel( - [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: opacityTransition }), - new Effect.MoveBy(effect.element, moveY, moveX, { sync: true, transition: moveTransition }), - new Effect.Scale(effect.element, 100, { - scaleMode: { originalHeight: originalHeight, originalWidth: originalWidth }, - sync: true, scaleFrom: window.opera ? 1 : 0, transition: scaleTransition, restoreAfterFinish: true}) - ], Object.extend({ - beforeSetup: function(effect) { - effect.effects[0].element.style.height = 0; - Element.show(effect.effects[0].element); - }, - afterFinishInternal: function(effect) { - var el = effect.effects[0].element; - var els = el.style; - Element.undoClipping(el); - Element.undoPositioned(el); - els.top = oldTop; - els.left = oldLeft; - els.height = oldHeight; - els.width = originalWidth + 'px'; - Element.setInlineOpacity(el, oldOpacity); - } - }, options) - ) - } - }); -} - -Effect.Shrink = function(element) { - element = $(element); - var options = arguments[1] || {}; - - var originalWidth = element.clientWidth; - var originalHeight = element.clientHeight; - var oldTop = element.style.top; - var oldLeft = element.style.left; - var oldHeight = element.style.height; - var oldWidth = element.style.width; - var oldOpacity = Element.getInlineOpacity(element); - - var direction = options.direction || 'center'; - var moveTransition = options.moveTransition || Effect.Transitions.sinoidal; - var scaleTransition = options.scaleTransition || Effect.Transitions.sinoidal; - var opacityTransition = options.opacityTransition || Effect.Transitions.none; - - var moveX, moveY; - - switch (direction) { - case 'top-left': - moveX = moveY = 0; - break; - case 'top-right': - moveX = originalWidth; - moveY = 0; - break; - case 'bottom-left': - moveX = 0; - moveY = originalHeight; - break; - case 'bottom-right': - moveX = originalWidth; - moveY = originalHeight; - break; - case 'center': - moveX = originalWidth / 2; - moveY = originalHeight / 2; - break; - } - - return new Effect.Parallel( - [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: opacityTransition }), - new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: scaleTransition, restoreAfterFinish: true}), - new Effect.MoveBy(element, moveY, moveX, { sync: true, transition: moveTransition }) - ], Object.extend({ - beforeStartInternal: function(effect) { - Element.makePositioned(effect.effects[0].element); - Element.makeClipping(effect.effects[0].element); - }, - afterFinishInternal: function(effect) { - var el = effect.effects[0].element; - var els = el.style; - Element.hide(el); - Element.undoClipping(el); - Element.undoPositioned(el); - els.top = oldTop; - els.left = oldLeft; - els.height = oldHeight; - els.width = oldWidth; - Element.setInlineOpacity(el, oldOpacity); - } - }, options) - ); -} - -Effect.Pulsate = function(element) { - element = $(element); - var options = arguments[1] || {}; - var oldOpacity = Element.getInlineOpacity(element); - var transition = options.transition || Effect.Transitions.sinoidal; - var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) }; - reverser.bind(transition); - return new Effect.Opacity(element, - Object.extend(Object.extend({ duration: 3.0, from: 0, - afterFinishInternal: function(effect) { Element.setInlineOpacity(effect.element, oldOpacity); } - }, options), {transition: reverser})); -} - -Effect.Fold = function(element) { - element = $(element); - var originalTop = element.style.top; - var originalLeft = element.style.left; - var originalWidth = element.style.width; - var originalHeight = element.style.height; - Element.makeClipping(element); - return new Effect.Scale(element, 5, Object.extend({ - scaleContent: false, - scaleX: false, - afterFinishInternal: function(effect) { - new Effect.Scale(element, 1, { - scaleContent: false, - scaleY: false, - afterFinishInternal: function(effect) { - Element.hide(effect.element); - Element.undoClipping(effect.element); - effect.element.style.top = originalTop; - effect.element.style.left = originalLeft; - effect.element.style.width = originalWidth; - effect.element.style.height = originalHeight; - } }); - }}, arguments[1] || {})); -} diff --git a/thirdparty/scriptaculous/scriptaculous.js b/thirdparty/scriptaculous/scriptaculous.js deleted file mode 100644 index 5290a8893..000000000 --- a/thirdparty/scriptaculous/scriptaculous.js +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -var Scriptaculous = { - Version: '1.5_rc5', - require: function(libraryName) { - // inserting via DOM fails in Safari 2.0, so brute force approach - document.write(''); - }, - load: function() { - if((typeof Prototype=='undefined') || - parseFloat(Prototype.Version.split(".")[0] + "." + - Prototype.Version.split(".")[1]) < 1.4) - throw("script.aculo.us requires the Prototype JavaScript framework >= 1.4.0"); - var scriptTags = document.getElementsByTagName("script"); - for(var i=0;i= this.allowedValues.max()) return(this.allowedValues.max()); - if(value <= this.allowedValues.min()) return(this.allowedValues.min()); - - var offset = Math.abs(this.allowedValues[0] - value); - var newValue = this.allowedValues[0]; - this.allowedValues.each( function(v) { - var currentOffset = Math.abs(v - value); - if(currentOffset <= offset){ - newValue = v; - offset = currentOffset; - } - }); - return newValue; - } - if(value > this.range.end) return this.range.end; - if(value < this.range.start) return this.range.start; - return value; - }, - setValue: function(sliderValue, handleIdx){ - if(!this.active) { - this.activeHandle = this.handles[handleIdx]; - this.activeHandleIdx = handleIdx; - this.updateStyles(); - } - handleIdx = handleIdx || this.activeHandleIdx || 0; - if(this.initialized && this.restricted) { - if((handleIdx>0) && (sliderValuethis.values[handleIdx+1])) - sliderValue = this.values[handleIdx+1]; - } - sliderValue = this.getNearestValue(sliderValue); - this.values[handleIdx] = sliderValue; - this.value = this.values[0]; // assure backwards compat - - this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] = - this.translateToPx(sliderValue); - - this.drawSpans(); - if(!this.dragging || !this.event) this.updateFinished(); - }, - setValueBy: function(delta, handleIdx) { - this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta, - handleIdx || this.activeHandleIdx || 0); - }, - translateToPx: function(value) { - return Math.round((this.trackLength / (this.range.end - this.range.start)) * (value - this.range.start)) + "px"; - }, - translateToValue: function(offset) { - return ((offset/this.trackLength) * (this.range.end - this.range.start)) + this.range.start; - }, - getRange: function(range) { - var v = this.values.sortBy(Prototype.K); - range = range || 0; - return $R(v[range],v[range+1]); - }, - minimumOffset: function(){ - return(this.isVertical() ? this.alignY : this.alignX); - }, - maximumOffset: function(){ - return(this.isVertical() ? - this.track.offsetHeight - this.alignY : this.track.offsetWidth - this.alignX); - }, - isVertical: function(){ - return (this.axis == 'vertical'); - }, - drawSpans: function() { - var slider = this; - if(this.spans) - $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) }); - if(this.options.startSpan) - this.setSpan(this.options.startSpan, - $R(0, this.values.length>1 ? this.getRange(0).min() : this.value )); - if(this.options.endSpan) - this.setSpan(this.options.endSpan, - $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum)); - }, - setSpan: function(span, range) { - if(this.isVertical()) { - span.style.top = this.translateToPx(range.start); - span.style.height = this.translateToPx(range.end - range.start); - } else { - span.style.left = this.translateToPx(range.start); - span.style.width = this.translateToPx(range.end - range.start); - } - }, - updateStyles: function() { - this.handles.each( function(h){ Element.removeClassName(h, 'selected') }); - Element.addClassName(this.activeHandle, 'selected'); - }, - startDrag: function(event) { - if(Event.isLeftClick(event)) { - if(!this.disabled){ - this.active = true; - - var handle = Event.element(event); - var pointer = [Event.pointerX(event), Event.pointerY(event)]; - if(handle==this.track) { - var offsets = Position.cumulativeOffset(this.track); - this.event = event; - this.setValue(this.translateToValue( - this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0] - )); - } else { - // find the handle (prevents issues with Safari) - while((this.handles.indexOf(handle) == -1) && handle.parentNode) - handle = handle.parentNode; - - this.activeHandle = handle; - this.activeHandleIdx = this.handles.indexOf(this.activeHandle); - this.updateStyles(); - - var offsets = Position.cumulativeOffset(this.activeHandle); - this.offsetX = (pointer[0] - offsets[0]); - this.offsetY = (pointer[1] - offsets[1]); - } - } - Event.stop(event); - } - }, - update: function(event) { - if(this.active) { - if(!this.dragging) this.dragging = true; - this.draw(event); - // fix AppleWebKit rendering - if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); - Event.stop(event); - } - }, - draw: function(event) { - var pointer = [Event.pointerX(event), Event.pointerY(event)]; - var offsets = Position.cumulativeOffset(this.track); - pointer[0] -= this.offsetX + offsets[0]; - pointer[1] -= this.offsetY + offsets[1]; - this.event = event; - this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] )); - if(this.initialized && this.options.onSlide) this.options.onSlide(this.values.length>1 ? this.values : this.value, this); - }, - endDrag: function(event) { - if(this.active && this.dragging) { - this.finishDrag(event, true); - Event.stop(event); - } - this.active = false; - this.dragging = false; - }, - finishDrag: function(event, success) { - this.active = false; - this.dragging = false; - this.updateFinished(); - }, - updateFinished: function() { - if(this.initialized && this.options.onChange) - this.options.onChange(this.values.length>1 ? this.values : this.value, this); - this.event = null; - } -} diff --git a/thirdparty/scriptaculous/unittest.js b/thirdparty/scriptaculous/unittest.js deleted file mode 100644 index 20941ad34..000000000 --- a/thirdparty/scriptaculous/unittest.js +++ /dev/null @@ -1,363 +0,0 @@ -// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) -// (c) 2005 Jon Tirsen (http://www.tirsen.com) -// (c) 2005 Michael Schuerig (http://www.schuerig.de/michael/) -// -// See scriptaculous.js for full license. - -// experimental, Firefox-only -Event.simulateMouse = function(element, eventName) { - var options = Object.extend({ - pointerX: 0, - pointerY: 0, - buttons: 0 - }, arguments[2] || {}); - var oEvent = document.createEvent("MouseEvents"); - oEvent.initMouseEvent(eventName, true, true, document.defaultView, - options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY, - false, false, false, false, 0, $(element)); - - if(this.mark) Element.remove(this.mark); - this.mark = document.createElement('div'); - this.mark.appendChild(document.createTextNode(" ")); - document.body.appendChild(this.mark); - this.mark.style.position = 'absolute'; - this.mark.style.top = options.pointerY + "px"; - this.mark.style.left = options.pointerX + "px"; - this.mark.style.width = "5px"; - this.mark.style.height = "5px;"; - this.mark.style.borderTop = "1px solid red;" - this.mark.style.borderLeft = "1px solid red;" - - if(this.step) - alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options)); - - $(element).dispatchEvent(oEvent); -}; - -// Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2. -// You need to downgrade to 1.0.4 for now to get this working -// See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much -Event.simulateKey = function(element, eventName) { - var options = Object.extend({ - ctrlKey: false, - altKey: false, - shiftKey: false, - metaKey: false, - keyCode: 0, - charCode: 0 - }, arguments[2] || {}); - - var oEvent = document.createEvent("KeyEvents"); - oEvent.initKeyEvent(eventName, true, true, window, - options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, - options.keyCode, options.charCode ); - $(element).dispatchEvent(oEvent); -}; - -Event.simulateKeys = function(element, command) { - for(var i=0; i ' + - (typeof obj[property] == "string" ? - '"' + obj[property] + '"' : - obj[property])); - } - - return ("'" + obj + "' #" + typeof obj + - ": {" + info.join(", ") + "}"); -} - -Test.Unit.Logger = Class.create(); -Test.Unit.Logger.prototype = { - initialize: function(log) { - this.log = $(log); - if (this.log) { - this._createLogTable(); - } - }, - start: function(testName) { - if (!this.log) return; - this.testName = testName; - this.lastLogLine = document.createElement('tr'); - this.statusCell = document.createElement('td'); - this.nameCell = document.createElement('td'); - this.nameCell.appendChild(document.createTextNode(testName)); - this.messageCell = document.createElement('td'); - this.lastLogLine.appendChild(this.statusCell); - this.lastLogLine.appendChild(this.nameCell); - this.lastLogLine.appendChild(this.messageCell); - this.loglines.appendChild(this.lastLogLine); - }, - finish: function(status, summary) { - if (!this.log) return; - this.lastLogLine.className = status; - this.statusCell.innerHTML = status; - this.messageCell.innerHTML = this._toHTML(summary); - }, - message: function(message) { - if (!this.log) return; - this.messageCell.innerHTML = this._toHTML(message); - }, - summary: function(summary) { - if (!this.log) return; - this.logsummary.innerHTML = this._toHTML(summary); - }, - _createLogTable: function() { - this.log.innerHTML = - '

    ' + - '' + - '' + - '' + - '
    StatusTestMessage
    '; - this.logsummary = $('logsummary') - this.loglines = $('loglines'); - }, - _toHTML: function(txt) { - return txt.escapeHTML().replace(/\n/g,"
    "); - } -} - -Test.Unit.Runner = Class.create(); -Test.Unit.Runner.prototype = { - initialize: function(testcases) { - this.options = Object.extend({ - testLog: 'testlog' - }, arguments[1] || {}); - this.options.resultsURL = this.parseResultsURLQueryParameter(); - if (this.options.testLog) { - this.options.testLog = $(this.options.testLog) || null; - } - if(this.options.tests) { - this.tests = []; - for(var i = 0; i < this.options.tests.length; i++) { - if(/^test/.test(this.options.tests[i])) { - this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"])); - } - } - } else { - if (this.options.test) { - this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])]; - } else { - this.tests = []; - for(var testcase in testcases) { - if(/^test/.test(testcase)) { - this.tests.push(new Test.Unit.Testcase(testcase, testcases[testcase], testcases["setup"], testcases["teardown"])); - } - } - } - } - this.currentTest = 0; - this.logger = new Test.Unit.Logger(this.options.testLog); - setTimeout(this.runTests.bind(this), 1000); - }, - parseResultsURLQueryParameter: function() { - return window.location.search.parseQuery()["resultsURL"]; - }, - // Returns: - // "ERROR" if there was an error, - // "FAILURE" if there was a failure, or - // "SUCCESS" if there was neither - getResult: function() { - var hasFailure = false; - for(var i=0;i 0) { - return "ERROR"; - } - if (this.tests[i].failures > 0) { - hasFailure = true; - } - } - if (hasFailure) { - return "FAILURE"; - } else { - return "SUCCESS"; - } - }, - postResults: function() { - if (this.options.resultsURL) { - new Ajax.Request(this.options.resultsURL, - { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false }); - } - }, - runTests: function() { - var test = this.tests[this.currentTest]; - if (!test) { - // finished! - this.postResults(); - this.logger.summary(this.summary()); - return; - } - if(!test.isWaiting) { - this.logger.start(test.name); - } - test.run(); - if(test.isWaiting) { - this.logger.message("Waiting for " + test.timeToWait + "ms"); - setTimeout(this.runTests.bind(this), test.timeToWait || 1000); - } else { - this.logger.finish(test.status(), test.summary()); - this.currentTest++; - // tail recursive, hopefully the browser will skip the stackframe - this.runTests(); - } - }, - summary: function() { - var assertions = 0; - var failures = 0; - var errors = 0; - var messages = []; - for(var i=0;i 0) return 'failed'; - if (this.errors > 0) return 'error'; - return 'passed'; - }, - assert: function(expression) { - var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"'; - try { expression ? this.pass() : - this.fail(message); } - catch(e) { this.error(e); } - }, - assertEqual: function(expected, actual) { - var message = arguments[2] || "assertEqual"; - try { (expected == actual) ? this.pass() : - this.fail(message + ': expected "' + Test.Unit.inspect(expected) + - '", actual "' + Test.Unit.inspect(actual) + '"'); } - catch(e) { this.error(e); } - }, - assertNotEqual: function(expected, actual) { - var message = arguments[2] || "assertNotEqual"; - try { (expected != actual) ? this.pass() : - this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); } - catch(e) { this.error(e); } - }, - assertNull: function(obj) { - var message = arguments[1] || 'assertNull' - try { (obj==null) ? this.pass() : - this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); } - catch(e) { this.error(e); } - }, - assertHidden: function(element) { - var message = arguments[1] || 'assertHidden'; - this.assertEqual("none", element.style.display, message); - }, - assertNotNull: function(object) { - var message = arguments[1] || 'assertNotNull'; - this.assert(object != null, message); - }, - assertInstanceOf: function(expected, actual) { - var message = arguments[2] || 'assertInstanceOf'; - try { - (actual instanceof expected) ? this.pass() : - this.fail(message + ": object was not an instance of the expected type"); } - catch(e) { this.error(e); } - }, - assertNotInstanceOf: function(expected, actual) { - var message = arguments[2] || 'assertNotInstanceOf'; - try { - !(actual instanceof expected) ? this.pass() : - this.fail(message + ": object was an instance of the not expected type"); } - catch(e) { this.error(e); } - }, - _isVisible: function(element) { - element = $(element); - if(!element.parentNode) return true; - this.assertNotNull(element); - if(element.style && Element.getStyle(element, 'display') == 'none') - return false; - - return this._isVisible(element.parentNode); - }, - assertNotVisible: function(element) { - this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1])); - }, - assertVisible: function(element) { - this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1])); - } -} - -Test.Unit.Testcase = Class.create(); -Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), { - initialize: function(name, test, setup, teardown) { - Test.Unit.Assertions.prototype.initialize.bind(this)(); - this.name = name; - this.test = test || function() {}; - this.setup = setup || function() {}; - this.teardown = teardown || function() {}; - this.isWaiting = false; - this.timeToWait = 1000; - }, - wait: function(time, nextPart) { - this.isWaiting = true; - this.test = nextPart; - this.timeToWait = time; - }, - run: function() { - try { - try { - if (!this.isWaiting) this.setup.bind(this)(); - this.isWaiting = false; - this.test.bind(this)(); - } finally { - if(!this.isWaiting) { - this.teardown.bind(this)(); - } - } - } - catch(e) { this.error(e); } - } -}); \ No newline at end of file