00001 <?php
00002
00009
00010
00012 define('FC_EVENT_UPDATE', 1);
00014 define('FC_EVENT_CLICK', 2);
00016 define('FC_EVENT_SELECT', 4);
00018 define('FC_EVENT_DESELECT', 8);
00019
00020
00029 function gen_control_name($as_array = false)
00030 {
00031 $control_name = md5(uniqid(mt_rand(), true));
00032 if ($as_array)
00033 $control_name .= '[]';
00034
00035 return $control_name;
00036 }
00037
00044 function is_valid_control_name($name, $multiple = false)
00045 {
00046 if (!$multiple)
00047 return preg_match('#^\w+$#', $name);
00048 else
00049 return preg_match('#^\w+\[\]$#', $name);
00050 }
00051
00058 function is_subclass($child, $parent) {
00059 if(is_string($child)) {
00060 $child = strtolower($child);
00061 $parent = strtolower($parent);
00062 do {
00063 if (!$child = get_parent_class($child))
00064 return false;
00065 } while ($child != $parent);
00066 return true;
00067 }
00068
00069 return is_subclass_of($child, $parent);
00070 }
00071
00081 function check_class($unknown, $base)
00082 {
00083 if (!class_exists($unknown))
00084 trigger_error("The class '$unknown' has not been declared", E_USER_ERROR);
00085 if (!is_subclass($unknown, $base))
00086 if ($unknown != $base)
00087 trigger_error("'$unknown' is not a '$base' or subclass thereof", E_USER_ERROR);
00088 }
00089
00101 class XHTMLElement
00102 {
00104 var $dom_document;
00106 var $node;
00108 var $serialise;
00109
00114 function XHTMLElement($name)
00115 {
00116 $this->dom_document = domxml_new_doc("1.0");
00117 $element = $this->dom_document->create_element($name);
00118 $this->node = $this->dom_document->append_child($element);
00119 }
00120
00125 function getElementName()
00126 {
00127 return $this->node->node_name();
00128 }
00129
00135 function getAttribute($name)
00136 {
00137 return $this->node->get_attribute($name);
00138 }
00139
00146 function setAttribute($name, $value)
00147 {
00148 $this->node->set_attribute($name, $value);
00149 }
00150
00157 function setBooleanAttribute($name, $state)
00158 {
00159 if ($state)
00160 $this->node->set_attribute($name, $name);
00161 else
00162 $this->removeAttribute($name);
00163 }
00164
00170 function hasAttribute($name)
00171 {
00172 return $this->node->has_attribute($name);
00173 }
00174
00180 function removeAttribute($name)
00181 {
00182 return $this->node->remove_attribute($name);
00183 }
00184
00189 function toXHTML()
00190 {
00191 return $this->dom_document->dump_node($this->node);
00192 }
00193
00194
00195
00196
00197
00203 function toString()
00204 {
00205 return $this->toXHTML();
00206 }
00207
00208
00209
00210
00211
00221 function __sleep()
00222 {
00223 $this->serialise = $this->toXHTML();
00224 return array('serialise');
00225 }
00226
00236 function __wakeup()
00237 {
00238 $this->dom_document = domxml_open_mem($this->serialise);
00239 $this->node = $this->dom_document->document_element();
00240 $this->serialise = null;
00241 }
00242 }
00243
00261 class ControlElement extends XHTMLElement
00262 {
00264 var $actions;
00266 var $events;
00268 var $display;
00269
00275 function ControlElement($name, $control_name = null)
00276 {
00277 $this->XHTMLElement($name);
00278
00279 $control_name = gen_control_name($this->submitsMultipleValues());
00280
00281 $this->setControlName($control_name);
00282 $this->setDisplay(true);
00283 $this->actions = array();
00284 $this->events = array();
00285
00286 $this->addEvent(FC_EVENT_UPDATE);
00287 }
00288
00289
00290
00291
00292
00297 function getId()
00298 {
00299 return $this->getAttribute('id');
00300 }
00301
00306 function setId($id)
00307 {
00308 $this->setAttribute('id', $id);
00309 }
00310
00317 function isSimpleControl()
00318 {
00319 return true;
00320 }
00321
00328 function submitsMultipleValues()
00329 {
00330 return false;
00331 }
00332
00337 function display()
00338 {
00339 return $this->display;
00340 }
00341
00347 function setDisplay($display)
00348 {
00349 if ($display)
00350 $this->display = true;
00351 else
00352 $this->display = false;
00353 }
00354
00359 function disabled()
00360 {
00361 return $this->hasAttribute('disabled');
00362 }
00363
00369 function setDisabled($disabled)
00370 {
00371 $this->setBooleanAttribute('disabled', $disabled);
00372 }
00373
00379 function updated()
00380 {
00381 return $this->eventHasOccurred(FC_EVENT_UPDATE);
00382 }
00383
00390 function setUpdated($updated)
00391 {
00392 if ($updated)
00393 $this->setEventHasOccurred(FC_EVENT_UPDATE, true);
00394 else
00395 $this->setEventHasOccurred(FC_EVENT_UPDATE, false);
00396 }
00397
00404 function checkControlName($name)
00405 {
00406 if (!is_valid_control_name($name, $this->submitsMultipleValues()))
00407 trigger_error("The name '$name' is not a valid control name", E_USER_ERROR);
00408 }
00409
00414 function getControlName()
00415 {
00416 return $this->getAttribute('name');
00417 }
00418
00424 function setControlName($name)
00425 {
00426 $this->checkControlName($name);
00427 $this->setAttribute('name', $name);
00428 }
00429
00436 function getValue()
00437 {
00438 }
00439
00446 function setValue($value)
00447 {
00448 $this->setUpdated(true);
00449 }
00450
00451
00452
00463 function addAction($event, $name, $callback)
00464 {
00465 if ($this->actionExists($name))
00466 trigger_error("The action '$name' already exists", E_USER_ERROR);
00467
00468 if (!is_callable($callback))
00469 trigger_error("The callback function to associate with the action '$name' is not valid", E_USER_ERROR);
00470
00471 if (!$this->isValidEvent($event))
00472 trigger_error("The event to associate with the action '$name' is not valid", E_USER_ERROR);
00473
00474 $this->actions[$name] = array($callback, $event, false);
00475 }
00476
00483 function setActionTriggered($name, $state)
00484 {
00485 if (!$this->actionExists($name))
00486 trigger_error("The action '$name' does not exist", E_USER_ERROR);
00487
00488 $this->actions[$name][2] = ($state) ? true : false;
00489 }
00490
00496 function actionTriggered($name)
00497 {
00498 if ($this->actionExists($name))
00499 return $this->actions[$name][2];
00500
00501 return false;
00502 }
00503
00508 function &getAction($name)
00509 {
00510 if ($this->actionExists($name))
00511 return $this->actions[$name][0];
00512
00513 return false;
00514 }
00515
00523 function getEvent($name)
00524 {
00525 if ($this->actionExists($name))
00526 return $this->actions[$name][1];
00527
00528 return false;
00529 }
00530
00541 function getActionNames($event = 0)
00542 {
00543 if (!$event)
00544 return array_keys($this->actions);
00545
00546 if (!$this->isValidEvent($event))
00547 trigger_error("The event is not valid", E_USER_ERROR);
00548
00549 $names = array();
00550 foreach (array_keys($this->actions) as $name)
00551 {
00552 if (($event ^ $this->getEvent($name)) !== 0)
00553 $names[] = $name;
00554 }
00555 return $names;
00556 }
00557
00568 function getActions($event = 0)
00569 {
00570 $names = $this->getActionNames($event);
00571 $actions = array();
00572 foreach ($names as $name)
00573 {
00574 $actions[] =& $this->getAction($name);
00575 }
00576
00577 return $actions;
00578 }
00579
00585 function actionExists($name)
00586 {
00587 return isset($this->actions[$name]);
00588 }
00589
00598 function triggerAction($name)
00599 {
00600 if (!$this->actionExists($name))
00601 trigger_error("The action '$name' does not exist", E_USER_ERROR);
00602
00603 if ($this->actionTriggered($name))
00604 return false;
00605
00606 $event = $this->getEvent($name);
00607
00608 if (!$this->eventHasOccurred($event))
00609 return false;
00610
00611 $this->setActionTriggered($name, true);
00612
00613 $callback =& $this->getAction($name);
00614 return call_user_func($callback, &$this);
00615 }
00616
00623 function removeAction($name)
00624 {
00625 unset($this->actions[$name]);
00626 }
00627
00634 function triggerActions()
00635 {
00636 foreach ($this->getActionNames() as $action)
00637 {
00638 $this->triggerAction($action);
00639 }
00640 }
00641
00642
00643
00651 function addEvent($event)
00652 {
00653 if ($this->isValidEvent($event))
00654 trigger_error("The event is not valid", E_USER_ERROR);
00655
00656 $this->events[$event] = false;
00657 }
00658
00663 function eventHasOccurred($event)
00664 {
00665 return $this->events[$event];
00666 }
00667
00674 function setEventHasOccurred($event, $state)
00675 {
00676 if (!$this->isValidEvent($event))
00677 trigger_error("The event is not supported", E_USER_ERROR);
00678
00679 $this->events[$event] = ($state) ? true : false;
00680
00681
00682 foreach ($this->getActionNames($event) as $action)
00683 {
00684 $this->setActionTriggered($action, false);
00685 }
00686 }
00687
00692 function supportedEvents()
00693 {
00694 $events = array_keys($this->events);
00695 $bits = 0;
00696
00697 foreach ($events as $event)
00698 {
00699 $bits = $bits ^ $event;
00700 }
00701
00702 return $bits;
00703 }
00704
00710 function isValidEvent($event)
00711 {
00712 return ($event & $this->supportedEvents()) !== 0;
00713 }
00714
00721 function resetEvents()
00722 {
00723
00724 $events = array_keys($this->events);
00725
00726 foreach ($events as $event)
00727 {
00728 $this->setEventHasOccurred($event, false);
00729 }
00730
00731
00732 foreach ($this->getActionNames() as $action)
00733 {
00734 $this->setActionTriggered($action, false);
00735 }
00736 }
00737
00738
00739
00740
00741
00749 function _processPOSTED($post)
00750 {
00751 if ($post === null)
00752 return;
00753
00754 if (!$this->submitsMultipleValues() and get_magic_quotes_gpc())
00755 $post = stripslashes($post);
00756
00757 if ($post != $this->getValue()) {
00758
00759 $this->setValue($post);
00760 }
00761 }
00762
00769 function __wakeup()
00770 {
00771 parent::__wakeup();
00772
00773 $this->resetEvents();
00774
00775 $name = $this->getControlName();
00776
00777 if (substr($name, -2) == '[]')
00778 $name = substr($name, 0, -2);
00779
00780 if (isset($_POST[$name]))
00781 $this->_processPOSTED($_POST[$name]);
00782 else
00783 $this->_processPOSTED(null);
00784 }
00785
00786 function __sleep()
00787 {
00788 $sleep_vars = parent::__sleep();
00789 array_push($sleep_vars, 'actions', 'display', 'events');
00790 return $sleep_vars;
00791 }
00792 }
00793
00798 class TextArea extends ControlElement
00799 {
00804 function TextArea($value = '')
00805 {
00806 $this->ControlElement('textarea');
00807 $this->setValue($value);
00808 }
00809
00810
00811
00812
00813
00814 function getValue()
00815 {
00816 return $this->node->get_content();
00817 }
00818
00819 function setValue($value)
00820 {
00821 $this->setUpdated(true);
00822
00823 if ($this->node->has_child_nodes()) {
00824 $text_node = $this->node->first_child();
00825 return $text_node->set_content($value);
00826 }
00827 return $this->node->set_content($value);
00828 }
00829 }
00830
00835 class Input extends ControlElement
00836 {
00842 function Input($type, $control_name = null)
00843 {
00844 $this->ControlElement('input', $control_name);
00845 $this->setAttribute('type', $type);
00846 }
00847 }
00848
00854 class ValueInput extends Input
00855 {
00859 function ValueInput($type, $control_name = null)
00860 {
00861 $this->Input($type, $control_name);
00862 }
00863
00864
00865
00866
00867
00868 function getValue()
00869 {
00870 return $this->getAttribute('value');
00871 }
00872
00873 function setValue($value)
00874 {
00875 $this->setUpdated(true);
00876
00877 $this->setAttribute('value', $value);
00878 }
00879 }
00880
00883 class TextInput extends ValueInput
00884 {
00889 function TextInput($value = null)
00890 {
00891 $this->ValueInput('text');
00892 $this->SetValue($value);
00893 }
00894 }
00895
00898 class PasswordInput extends ValueInput
00899 {
00904 function PasswordInput($value = null)
00905 {
00906 $this->ValueInput('password');
00907 $this->SetValue($value);
00908 }
00909 }
00910
00913 class HiddenInput extends ValueInput
00914 {
00919 function HiddenInput($value = null)
00920 {
00921 $this->ValueInput('hidden');
00922 $this->setValue($value);
00923 }
00924
00925
00926
00927
00928
00935 function setDisabled($disabled)
00936 {
00937 }
00938 }
00939
00945 class ClickedInput extends ValueInput
00946 {
00952 function ClickedInput($type, $value, $control_name = null)
00953 {
00954 $this->ValueInput($type, $control_name);
00955 $this->setValue($value);
00956
00957 $this->addEvent(FC_EVENT_CLICK);
00958 }
00959
00966 function clicked()
00967 {
00968 return $this->eventHasOccurred(FC_EVENT_CLICK);
00969 }
00970
00971
00972
00973
00974
00975 function _processPOSTED($post)
00976 {
00977 if ($post !== null) {
00978 $this->setEventHasOccurred(FC_EVENT_CLICK, true);
00979 parent::_processPOSTED($post);
00980 } else {
00981 $this->setEventHasOccurred(FC_EVENT_CLICK, false);
00982 }
00983 }
00984 }
00985
00988 class SubmitInput extends ClickedInput
00989 {
00994 function SubmitInput($value)
00995 {
00996 $this->ClickedInput('submit', $value);
00997 }
00998 }
00999
01002 class ImageInput extends ClickedInput
01003 {
01005 var $value;
01006
01012 function ImageInput($src = null, $control_name = null)
01013 {
01014 $this->ClickedInput('image', null);
01015 if ($src !== null)
01016 $this->setSrc($src);
01017 }
01018
01023 function getXY()
01024 {
01025 return array('x' => $this->value[0],
01026 'y' => $this->value[1]);
01027 }
01028
01033 function getX()
01034 {
01035 return $this->value[0];
01036 }
01037
01042 function getY()
01043 {
01044 return $this->value[1];
01045 }
01046
01047
01048
01049
01050
01055 function getSrc()
01056 {
01057 return $this->getAttribute('src');
01058 }
01059
01065 function setSrc($src)
01066 {
01067 $this->setAttribute('src', $src);
01068 }
01069
01070
01071
01072
01073
01074
01075 function submitsMultipleValues()
01076 {
01077 return true;
01078 }
01079
01080 function getValue()
01081 {
01082 return $this->value;
01083 }
01084
01085 function setValue($value)
01086 {
01087 if (!is_array($value)) {
01088 $this->value = array();
01089 return;
01090 }
01091 if (sizeof($value) != 2)
01092 trigger_error("The value array must consist of two elements", E_USER_ERROR);
01093 if (!is_numeric($value[0]) or !is_numeric($value[0]))
01094 trigger_error("The value elements must be numeric", E_USER_ERROR);
01095
01096 $this->setUpdated(true);
01097
01098 $this->value = $value;
01099 }
01100 }
01101
01108 class EnhancedImageInput extends ImageInput
01109 {
01111 var $enabled_src;
01113 var $disabled_src;
01114
01121 function EnhancedImageInput($enabled_src, $disabled_src, $control_name = null)
01122 {
01123 $this->ImageInput($enabled_src, $control_name = null) ;
01124 $this->enabled_src = $enabled_src;
01125 $this->disabled_src = $disabled_src;
01126 }
01127
01128
01129
01130
01131
01132 function setDisabled($disabled)
01133 {
01134 parent::setDisabled($disabled);
01135
01136 $this->setSrc((!$this->disabled()) ? $this->enabled_src : $this->disabled_src);
01137 }
01138
01139
01140
01141
01142
01143 function __sleep()
01144 {
01145 $sleep_vars = parent::__sleep();
01146 array_push($sleep_vars,
01147 'enabled_src',
01148 'disabled_src');
01149 return $sleep_vars;
01150 }
01151
01152 }
01153
01156 class ResetInput extends ValueInput
01157 {
01161 function ResetInput($value)
01162 {
01163 $this->ValueInput('reset');
01164 $this->setValue($value);
01165 }
01166 }
01167
01177 class Option extends ControlElement
01178 {
01180 var $control_name;
01181
01188 function Option($value, $label = null, $control_name = null)
01189 {
01190 $this->ControlElement('option', $control_name);
01191 $this->setLabel($label);
01192 $this->setValue($value);
01193
01194 $this->addEvent(FC_EVENT_SELECT);
01195 $this->addEvent(FC_EVENT_DESELECT);
01196 }
01197
01198
01199
01200
01201
01202 function getValue()
01203 {
01204 return $this->getAttribute('value');
01205 }
01206
01207 function setValue($value)
01208 {
01209 $this->setUpdated(true);
01210
01211 $this->setAttribute('value', $value);
01212 }
01213
01214
01215
01216 function getControlName()
01217 {
01218 return $this->control_name;
01219 }
01220
01221 function setControlName($name)
01222 {
01223 $this->checkControlName($name);
01224 $this->control_name = $name;
01225 }
01226
01227
01228
01229
01230
01235 function getLabel()
01236 {
01237 return $this->node->get_content();
01238 }
01239
01245 function setLabel($label)
01246 {
01247 if ($this->node->has_child_nodes()) {
01248 $text_node = $this->node->first_child();
01249 return $text_node->set_content($label);
01250 }
01251 return $this->node->set_content($label);
01252 }
01253
01258 function selected()
01259 {
01260 return $this->hasAttribute('selected');
01261 }
01262
01267 function select()
01268 {
01269 $this->setBooleanAttribute('selected', true);
01270 $this->setEventHasOccurred(FC_EVENT_SELECT, true);
01271 $this->setEventHasOccurred(FC_EVENT_DESELECT, false);
01272 }
01273
01278 function deselect()
01279 {
01280 $this->setBooleanAttribute('selected', false);
01281 $this->setEventHasOccurred(FC_EVENT_DESELECT, true);
01282 $this->setEventHasOccurred(FC_EVENT_SELECT, false);
01283 }
01284
01285
01286
01287
01288
01289 function __sleep()
01290 {
01291 $sleep_vars = parent::__sleep();
01292 array_push($sleep_vars, 'control_name');
01293 return $sleep_vars;
01294 }
01295 }
01296
01302 class SingleOption extends Option
01303 {
01307 function SingleOption($value, $label = null, $control_name = null)
01308 {
01309 $this->Option($value, $label, $control_name);
01310 }
01311
01312
01313
01314
01315
01316 function _processPOSTED($post)
01317 {
01318
01319
01320
01321 if ($post === null)
01322 return;
01323
01324
01325 if ($post == $this->getValue()) {
01326 if (!$this->selected())
01327 $this->select();
01328 } elseif ($this->selected()) {
01329 $this->deselect();
01330 }
01331 }
01332 }
01333
01339 class MultipleOption extends Option
01340 {
01344 function MultipleOption($value, $label = null, $control_name = null)
01345 {
01346 $this->Option($value, $label, $control_name);
01347 }
01348
01349
01350
01351
01352
01353 function submitsMultipleValues()
01354 {
01355 return true;
01356 }
01357
01358
01359
01360
01361
01362 function _processPOSTED($post)
01363 {
01364
01365
01366 if ($post === null)
01367 return;
01368
01369 if (is_array($post)) {
01370 if (in_array($this->getValue(), $post)) {
01371 if (!$this->selected()) {
01372 $this->select();
01373 }
01374 return;
01375 }
01376 }
01377
01378 $this->deselect();
01379 }
01380 }
01381
01389 class CheckedInput extends ValueInput
01390 {
01392 var $label;
01393
01399 function CheckedInput($type, $label = null, $control_name = null)
01400 {
01401 $this->ValueInput($type, $control_name);
01402 $this->setLabel($label);
01403
01404 $this->addEvent(FC_EVENT_SELECT);
01405 $this->addEvent(FC_EVENT_DESELECT);
01406 }
01407
01408
01409
01410
01411
01416 function getLabel()
01417 {
01418 return $this->label;
01419 }
01420
01425 function setLabel($label)
01426 {
01427 $this->label = $label;
01428 }
01429
01434 function selected()
01435 {
01436 return $this->hasAttribute('checked');
01437 }
01438
01443 function select()
01444 {
01445 $this->setBooleanAttribute('checked', true);
01446 $this->setEventHasOccurred(FC_EVENT_SELECT, true);
01447 $this->setEventHasOccurred(FC_EVENT_DESELECT, false);
01448 }
01449
01454 function deselect()
01455 {
01456 $this->setBooleanAttribute('checked', false);
01457 $this->setEventHasOccurred(FC_EVENT_DESELECT, true);
01458 $this->setEventHasOccurred(FC_EVENT_SELECT, false);
01459 }
01460
01461
01462
01463
01464
01465 function toString()
01466 {
01467 $xhtml = $this->toXHTML();
01468 $label = $this->getLabel();
01469 $id = $this->getId();
01470
01471 return sprintf('%s <label for="%s">%s</label>', $xhtml, $id, $label);
01472 }
01473
01474
01475
01476
01477
01478 function __sleep()
01479 {
01480 $sleep_vars = parent::__sleep();
01481 array_push($sleep_vars, 'label');
01482 return $sleep_vars;
01483 }
01484 }
01485
01486
01489 class RadioInput extends CheckedInput
01490 {
01494 function RadioInput($value, $label = null, $control_name = null)
01495 {
01496 $this->CheckedInput('radio', $label, $control_name);
01497 $this->setValue($value);
01498 }
01499
01500
01501
01502
01503
01504 function _processPOSTED($post)
01505 {
01506
01507
01508
01509 if ($post === null)
01510 return;
01511
01512
01513 if ($post == $this->getValue()) {
01514 if (!$this->selected())
01515 $this->select();
01516 } elseif ($this->selected()) {
01517 $this->deselect();
01518 }
01519 }
01520 }
01521
01524 class CheckboxInput extends CheckedInput
01525 {
01532 function CheckboxInput($value, $label = null, $control_name = null)
01533 {
01534 $this->CheckedInput('checkbox', $label, $control_name, true);
01535 $this->setValue($value);
01536 }
01537
01538
01539
01540
01541
01542 function submitsMultipleValues()
01543 {
01544 return true;
01545 }
01546
01547
01548
01549
01550
01551 function toString()
01552 {
01553 $string = parent::toString();
01554 return $string .= sprintf('<input type="hidden" name="%s" value="">', $this->getControlName());
01555 }
01556
01557
01558
01559
01560
01561 function _processPOSTED($post)
01562 {
01563
01564
01565 if ($post === null)
01566 return;
01567
01568 if (is_array($post)) {
01569 if (in_array($this->getValue(), $post)) {
01570 if (!$this->selected()) {
01571 $this->select();
01572 }
01573 return;
01574 }
01575 }
01576
01577 $this->deselect();
01578 }
01579
01580 }
01581
01584 class FileInput extends Input
01585 {
01587 var $filename;
01588
01589 function FileInput()
01590 {
01591 $this->Input('file');
01592 }
01593
01594
01595
01596
01597
01602 function getValue()
01603 {
01604 return $this->filename;
01605 }
01606
01613 function setValue($value)
01614 {
01615 $this->setUpdated(true);
01616
01617 $this->filename = $value;
01618 }
01619
01620
01621
01622
01623
01624 function __wakeup()
01625 {
01626 parent::__wakeup();
01627
01628 $name = $this->getControlName();
01629 $matches = preg_split('#\[(\w+|\d+)\]#', $name, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
01630 if (sizeof($matches) == 0)
01631 return;
01632
01633
01634 $root_name = array_shift($matches);
01635 if (!isset($_FILES[$root_name]))
01636 return;
01637
01638 $files =& $_FILES[$root_name];
01639 $upload_data = array();
01640
01641 foreach ($files as $info => $content)
01642 {
01643 if (!is_array($content)) {
01644 $upload_data[$info] = $content;
01645 continue;
01646 }
01647
01648 $final_val =& $content;
01649 foreach ($matches as $value) {
01650
01651 if (isset($final_val[$value])) {
01652 $final_val =& $final_val[$value];
01653 }
01654 }
01655 $upload_data[$info] = $final_val;
01656 }
01657
01658 $filename = $upload_data['tmp_name'].'/'.$upload_data['name'];
01659 if (is_uploaded_file($upload_data['tmp_name']))
01660 $this->setValue($filename);
01661 }
01662 }
01663
01669 class ControlContainer
01670 {
01672 var $controls;
01674 var $trigger_order;
01675
01676 function ControlContainer()
01677 {
01678 $this->controls = array();
01679 $this->trigger_order = array();
01680 }
01681
01682
01683
01684
01685
01691 function &get($id)
01692 {
01693 if (!$this->exists($id))
01694 trigger_error("The id '$id' does not exist", E_USER_ERROR);
01695 return $this->controls[$id];
01696 }
01697
01705 function &getByValue($value)
01706 {
01707 foreach ($this->getAllControls() as $control)
01708 {
01709 if ($control->submitsMultipleValues()) {
01710 if (in_array($value, $control->getValue()))
01711 return $control->getByValue($value);
01712 } elseif ($control->getValue() == $value) {
01713 return $control;
01714 }
01715 }
01716 return null;
01717 }
01718
01724 function add(&$control)
01725 {
01726 $id = $control->getId();
01727 if (!strlen($id))
01728 trigger_error("The control does not have an id", E_USER_ERROR);
01729
01730 if ($this->exists($id))
01731 trigger_error("The id '$id' already exists", E_USER_ERROR);
01732
01733 $this->controls[$id] =& $control;
01734
01735 $this->trigger_order[] = $control->getId();
01736 }
01737
01743 function exists($id)
01744 {
01745 return isset($this->controls[$id]);
01746 }
01747
01752 function count()
01753 {
01754 return sizeof($this->controls);
01755 }
01756
01762 function &remove($id)
01763 {
01764 if (!$this->exists($id))
01765 return false;
01766 $control =& $this->get($id);
01767 unset($this->controls[$id]);
01768
01769 $trigger_count = sizeof($this->trigger_order);
01770 for ($i = 0; $i < $trigger_count; $i++) {
01771 if ($this->trigger_order[$i] == $id) {
01772 if ($i == 0)
01773 array_shift($this->trigger_order);
01774 elseif ($i == $trigger_count - 1)
01775 array_pop($this->trigger_order);
01776 else {
01777 $this->trigger_order = array_merge(array_slice($this->trigger_order, 0, $i), array_slice($this->trigger_order, $i+1));
01778 }
01779 }
01780 }
01781
01782 return $control;
01783 }
01784
01789 function getAllIds()
01790 {
01791 return array_keys($this->controls);
01792 }
01793
01798 function getAllControls()
01799 {
01800 $controls = array();
01801
01802 foreach ($this->getAllIds() as $id)
01803 {
01804 $controls[] =& $this->controls[$id];
01805 }
01806
01807 return $controls;
01808 }
01809
01814 function getDisplayableIds()
01815 {
01816 $displayable = array();
01817
01818 foreach ($this->getAllIds() as $id)
01819 {
01820 if ($this->childDisplay($id))
01821 $displayable[] = $id;
01822 }
01823
01824 return $displayable;
01825 }
01826
01831 function getDisplayableControls()
01832 {
01833 $displayable = array();
01834
01835 foreach ($this->getDisplayableIds() as $id)
01836 {
01837 $displayable[] =& $this->get($id);
01838 }
01839
01840 return $displayable;
01841 }
01842
01843
01844
01850 function childDisplay($id)
01851 {
01852 $control =& $this->get($id);
01853 return $control->display();
01854 }
01855
01861 function childToString($id)
01862 {
01863 $control =& $this->get($id);
01864 return $control->toString();
01865 }
01866
01872 function childUpdated($id)
01873 {
01874 $control =& $this->get($id);
01875 return $control->updated();
01876 }
01877
01878
01879
01884 function triggerChildActions()
01885 {
01886 foreach ($this->getTriggerOrder() as $id)
01887 {
01888 $control =& $this->get($id);
01889 $control->triggerActions();
01890 }
01891 }
01892
01897 function getTriggerOrder()
01898 {
01899 return $this->trigger_order;
01900 }
01901
01907 function setTriggerOrder($order)
01908 {
01909 $control_count = $this->count();
01910
01911 if (sizeof($order) != $control_count)
01912 trigger_error('the action order does not contain the same number of elements as there are controls');
01913
01914 if (sizeof(array_unique($order)) != $control_count)
01915 trigger_error("The action order does not consist of unique values", E_USER_ERROR);
01916
01917 $new_order = array();
01918 foreach ($order as $id)
01919 {
01920 if (!$this->exists($id))
01921 trigger_error("A control with id '$id' does not exist", E_USER_ERROR);
01922
01923 $new_order[] = $id;
01924 }
01925 $this->order = $new_order;
01926 }
01927
01928
01929
01930
01931
01932 function __sleep()
01933 {
01934 return array('controls', 'trigger_order');
01935 }
01936 }
01937
01946 class ControlChoice extends ControlContainer
01947 {
01949 var $control_name;
01951 var $id;
01953 var $control_class;
01955 var $actions;
01957 var $selected;
01959 var $display;
01961 var $events;
01962
01968 function ControlChoice($control_class, $choice)
01969 {
01970 $this->ControlContainer();
01971 $control_name = gen_control_name($this->submitsMultipleValues());
01972 $this->setControlName($control_name);
01973 $this->setDisplay(true);
01974 $this->actions = array();
01975 $this->selected = array();
01976 $this->events = array();
01977
01978 $this->addEvent(FC_EVENT_UPDATE);
01979
01980 $this->setControlClass($control_class);
01981
01982 if (is_array($choice)) {
01983 $values = array();
01984
01985 foreach ($choice as $id => $object)
01986 {
01987
01988 $label = $value = null;
01989 if (is_array($object)) {
01990 if (sizeof($object) > 1) {
01991 $label = $object[0];
01992 $value = $object[1];
01993 } else {
01994 $label = $id;
01995 $value = $object[0];
01996 }
01997 } else {
01998 $label = $object;
01999 $value = $id;
02000 }
02001
02002 if (in_array($value, $values))
02003 trigger_error("Bad value for id '$id'; the value is not unique", E_USER_ERROR);
02004
02005 array_push($values, $value);
02006
02007 $control =& new $control_class($value, $label, $control_name);
02008 $control->setId($id);
02009 $this->add($control);
02010 }
02011 }
02012
02013 }
02014
02015
02016
02017
02018
02023 function isSimpleControl()
02024 {
02025 return false;
02026 }
02027
02032 function submitsMultipleValues()
02033 {
02034 return false;
02035 }
02036
02041 function display()
02042 {
02043 return $this->display;
02044 }
02045
02050 function setDisplay($display)
02051 {
02052 if ($display)
02053 $this->display = true;
02054 else
02055 $this->display = false;
02056 }
02057
02062 function checkControlName($name)
02063 {
02064 if (!is_valid_control_name($name, $this->submitsMultipleValues()))
02065 trigger_error("The name '$name' is not a valid control name", E_USER_ERROR);
02066 }
02067
02072 function setControlName($name)
02073 {
02074 $this->checkControlName($name);
02075 $this->control_name = $name;
02076
02077 foreach ($this->getAllIds() as $id)
02078 {
02079 $control =& $this->get($id);
02080 $control->setControlName($name);
02081 }
02082 }
02083
02088 function getControlName()
02089 {
02090 return $this->control_name;
02091 }
02092
02097 function updated()
02098 {
02099 return $this->eventHasOccurred(FC_EVENT_UPDATE);
02100 }
02101
02106 function getId()
02107 {
02108 return $this->id;
02109 }
02110
02115 function setId($id)
02116 {
02117 $this->id = $id;
02118 }
02119
02124 function getValue()
02125 {
02126 }
02127
02132 function setValue($value)
02133 {
02134 }
02135
02136
02137
02142 function addAction($event, $name, $callback)
02143 {
02144 if ($this->actionExists($name))
02145 trigger_error("The action '$name' already exists", E_USER_ERROR);
02146
02147 if (!is_callable($callback))
02148 trigger_error("The callback function to associate with the action '$name' is not valid", E_USER_ERROR);
02149
02150 if (!$this->isValidEvent($event))
02151 trigger_error("The event to associate with the action '$name' is not valid", E_USER_ERROR);
02152
02153 $this->actions[$name] = array($callback, $event, false);
02154 }
02155
02160 function setActionTriggered($name, $state)
02161 {
02162 if (!$this->actionExists($name))
02163 trigger_error("The action '$name' does not exist", E_USER_ERROR);
02164
02165 $this->actions[$name][2] = ($state) ? true : false;
02166 }
02167
02172 function actionTriggered($name)
02173 {
02174 if ($this->actionExists($name))
02175 return $this->actions[$name][2];
02176
02177 return false;
02178 }
02179
02184 function &getAction($name)
02185 {
02186 return $this->actions[$name][0];
02187 }
02188
02193 function getEvent($name)
02194 {
02195 return $this->actions[$name][1];
02196 }
02197
02202 function getActionNames($event = 0)
02203 {
02204 if (!$event)
02205 return array_keys($this->actions);
02206
02207 if (!$this->isValidEvent($event))
02208 trigger_error("The event is not valid", E_USER_ERROR);
02209
02210 $names = array();
02211 foreach (array_keys($this->actions) as $name)
02212 {
02213 if (($event ^ $this->getEvent($name)) !== 0)
02214 $names[] = $name;
02215 }
02216 return $names;
02217 }
02218
02223 function getActions($event = 0)
02224 {
02225 $names = $this->getActionNames($event);
02226 $actions = array();
02227 foreach ($names as $name)
02228 {
02229 $actions[] =& $this->getAction($name);
02230 }
02231
02232 return $actions;
02233 }
02234
02239 function actionExists($name)
02240 {
02241 return isset($this->actions[$name]);
02242 }
02243
02248 function triggerAction($name)
02249 {
02250 if (!$this->actionExists($name))
02251 trigger_error("The action '$name' does not exist", E_USER_ERROR);
02252
02253 if ($this->actionTriggered($name))
02254 return false;
02255
02256 $event = $this->getEvent($name);
02257
02258 if (!$this->eventHasOccurred($event))
02259 return false;
02260
02261 $this->setActionTriggered($name, true);
02262
02263 $callback =& $this->getAction($name);
02264 return call_user_func($callback, &$this);
02265 }
02266
02271 function removeAction($name)
02272 {
02273 unset($this->actions[$name]);
02274 }
02275
02280 function triggerActions()
02281 {
02282 foreach ($this->getActionNames() as $action)
02283 {
02284 $this->triggerAction($action);
02285 }
02286
02287 $this->triggerChildActions();
02288 }
02289
02290
02291
02296 function addEvent($event)
02297 {
02298 if ($this->isValidEvent($event))
02299 trigger_error("The event is already exists", E_USER_ERROR);
02300
02301 $this->events[$event] = false;
02302 }
02303
02308 function eventHasOccurred($event)
02309 {
02310 if ($event & FC_EVENT_UPDATE) {
02311 $current_selected = $this->getSelectedIds();
02312
02313
02314 if (sizeof($current_selected) != sizeof($this->selected))
02315 $this->setEventHasOccurred(FC_EVENT_UPDATE, true);
02316
02317
02318 if (sizeof(array_diff($current_selected, $this->selected)))
02319 $this->setEventHasOccurred(FC_EVENT_UPDATE, true);
02320 }
02321
02322 return $this->events[$event];
02323 }
02324
02329 function setEventHasOccurred($event, $state)
02330 {
02331 if (!$this->isValidEvent($event))
02332 trigger_error("The event is not supported", E_USER_ERROR);
02333
02334 $this->events[$event] = ($state) ? true : false;
02335
02336
02337 foreach ($this->getActionNames($event) as $action)
02338 {
02339 $this->setActionTriggered($action, false);
02340 }
02341 }
02342
02347 function supportedEvents()
02348 {
02349 $events = array_keys($this->events);
02350 $bits = 0;
02351
02352 foreach ($events as $event)
02353 {
02354 $bits = $bits ^ $event;
02355 }
02356
02357 return $bits;
02358 }
02359
02364 function isValidEvent($event)
02365 {
02366 return ($event & $this->supportedEvents()) !== 0;
02367 }
02368
02373 function resetEvents()
02374 {
02375
02376 $events = array_keys($this->events);
02377
02378 foreach ($events as $event)
02379 {
02380 $this->setEventHasOccurred($event, false);
02381 }
02382
02383
02384 foreach ($this->getActionNames() as $action)
02385 {
02386 $this->setActionTriggered($action, false);
02387 }
02388 }
02389
02390
02391
02392
02393
02394 function add(&$control)
02395 {
02396 parent::add($control);
02397 $control->setControlName($this->getControlName());
02398 }
02399
02400
02401
02402
02403
02409 function setControlClass($control_class)
02410 {
02411 $this->control_class = $control_class;
02412 }
02413
02414
02419 function getControlClass()
02420 {
02421 return $this->control_class;
02422 }
02423
02431 function &addChoice($value, $label = null, $selected = false)
02432 {
02433 $control_class = $this->getControlClass();
02434 $choice =& new $control_class($value, $label, $this->getControlName());
02435 $choice->setId($value);
02436
02437 if ($selected)
02438 $choice->select();
02439
02440 $this->add($choice);
02441 return $choice;
02442 }
02443
02448 function getSelectedIds()
02449 {
02450 $selected_ids = array();
02451 foreach ($this->getAllIds() as $id)
02452 {
02453 if ($this->selected($id))
02454 array_push($selected_ids, $id);
02455 }
02456 return $selected_ids;
02457 }
02458
02463 function getSelectedControls()
02464 {
02465 $selected_controls = array();
02466 foreach ($this->getSelectedIds() as $id)
02467 {
02468 $selected_controls[] =& $this->get($id);
02469 }
02470 return $selected_controls;
02471 }
02472
02477 function selectedCount()
02478 {
02479 return sizeof($this->getSelectedIds());
02480 }
02481
02487 function select($id)
02488 {
02489 $control =& $this->get($id);
02490 return $control->select();
02491 }
02492
02498 function selected($id)
02499 {
02500 $control =& $this->get($id);
02501 return $control->selected();
02502 }
02503
02509 function deselect($id)
02510 {
02511 $control =& $this->get($id);
02512 return $control->deselect();
02513 }
02514
02515
02516
02517
02518
02519
02521 function toString()
02522 {
02523 $strings = array();
02524
02525 foreach ($this->getDisplayableControls() as $control)
02526 {
02527 array_push($strings, $control->toString());
02528 }
02529
02530 return implode('<br/>', $strings);
02531 }
02532
02533
02534
02535
02536
02537
02542 function __wakeup()
02543 {
02544 $this->resetEvents();
02545 }
02546
02551 function __sleep()
02552 {
02553 $this->selected = $this->getSelectedIds();
02554
02555 $sleep_vars = parent::__sleep();
02556 array_push($sleep_vars, 'control_name', 'id', 'control_class', 'actions', 'selected', 'display', 'events');
02557 return $sleep_vars;
02558 }
02559 }
02560
02563 class SingleChoice extends ControlChoice
02564 {
02565
02570 function SingleChoice($control_class, $choice)
02571 {
02572 $this->ControlChoice($control_class, $choice);
02573 }
02574
02575
02576
02577
02578
02579 function getValue()
02580 {
02581 $selected = $this->getSelectedControls();
02582 if (sizeof($selected) == 1)
02583 return $selected[0]->getValue();
02584 return null;
02585 }
02586
02587
02588
02589
02590
02591 function select($id)
02592 {
02593 parent::select($id);
02594
02595 foreach ($this->getSelectedIds() as $selected_id)
02596 {
02597 if ($id != $selected_id)
02598 $this->deselect($selected_id);
02599 }
02600 }
02601 }
02602
02609 class RadioChoice extends SingleChoice
02610 {
02616 function RadioChoice($choice, $radio_class = 'RadioInput')
02617 {
02618 check_class($radio_class, 'RadioInput');
02619
02620 $this->SingleChoice($radio_class, $choice);
02621 }
02622 }
02623
02626 class MultipleChoice extends ControlChoice
02627 {
02632 function MultipleChoice($control_class, $choice)
02633 {
02634 $this->ControlChoice($control_class, $choice);
02635 }
02636
02637
02638
02639
02640
02641 function submitsMultipleValues()
02642 {
02643 return true;
02644 }
02645
02646 function getValue()
02647 {
02648 $selected = array();
02649 foreach ($this->getSelectedControls() as $control)
02650 {
02651 $selected[] = $control->getValue();
02652 }
02653 return $selected;
02654 }
02655
02656 }
02657
02664 class CheckboxChoice extends MultipleChoice
02665 {
02671 function CheckboxChoice($choice, $checkbox_class = 'CheckboxInput')
02672 {
02673 check_class($checkbox_class, 'CheckboxInput');
02674
02675 $this->MultipleChoice($checkbox_class, $choice);
02676 }
02677 }
02678
02679
02692 class SelectElement extends ControlElement
02693 {
02695 var $controlchoice;
02696
02701 function SelectElement($choice = null)
02702 {
02703 if ($this->submitsMultipleValues())
02704 $this->controlchoice = new MultipleChoice('MultipleOption', $choice);
02705 else
02706 $this->controlchoice = new SingleChoice('SingleOption', $choice);
02707
02708 $this->ControlElement('select', $this->controlchoice->getControlName());
02709 }
02710
02711 function setAttribute($name, $value)
02712 {
02713 if ($name == 'multiple') {
02714 if ($this->submitsMultipleValues() and $this->hasAttribute('multiple'))
02715 trigger_error("The 'multiple' attribute cannot be changed", E_USER_ERROR);
02716 }
02717
02718 parent::setAttribute($name, $value);
02719 }
02720
02721 function toXHTML()
02722 {
02723 $dom_document = $this->dom_document->clone_node(true);
02724 $root = $dom_document->document_element();
02725 foreach ($this->getDisplayableControls() as $option)
02726 {
02727 $node = $option->node->clone_node(true);
02728 $root->append_child($node);
02729 }
02730
02731 return $dom_document->dump_node($root);
02732 }
02733
02734
02735
02736
02737
02738 function isSimpleControl()
02739 {
02740 return false;
02741 }
02742
02743 function submitsMultipleValues()
02744 {
02745 return false;
02746 }
02747
02748 function setId($id)
02749 {
02750 parent::setId($id);
02751 $this->controlchoice->setId($id);
02752 }
02753
02754 function getValue()
02755 {
02756 return $this->controlchoice->getValue();
02757 }
02758
02759 function setControlName($name)
02760 {
02761 parent::setControlName($name);
02762 if ($this->controlchoice)
02763 $this->controlchoice->setControlName($name);
02764 }
02765
02766 function triggerActions()
02767 {
02768 parent::triggerActions();
02769 $this->triggerChildActions();
02770 }
02771
02772 function eventHasOccurred($event)
02773 {
02774 if ($event == FC_EVENT_UPDATE) {
02775 $this->setEventHasOccurred(FC_EVENT_UPDATE, $this->controlchoice->eventHasOccurred(FC_EVENT_UPDATE));
02776 }
02777
02778 return parent::eventHasOccurred($event);
02779 }
02780
02781
02782
02783
02784
02789 function setControlClass($control_class)
02790 {
02791 $this->controlchoice->setControlClass($control_class);
02792 }
02793
02794
02799 function getControlClass()
02800 {
02801 return $this->controlchoice->getControlClass();
02802 }
02803
02804
02809 function &addChoice($value, $label = null, $selected = false)
02810 {
02811 return $this->controlchoice->addChoice($value, $label, $selected);
02812 }
02813
02818 function selectedCount()
02819 {
02820 return $this->controlchoice->selectedCount();
02821 }
02822
02827 function getSelectedIds()
02828 {
02829 return $this->controlchoice->getSelectedIds();
02830 }
02831
02836 function getSelectedControls()
02837 {
02838 return $this->controlchoice->getSelectedControls();
02839 }
02840
02845 function select($id)
02846 {
02847 return $this->controlchoice->select($id);
02848 }
02849
02854 function selected($id)
02855 {
02856 return $this->controlchoice->selected($id);
02857 }
02858
02863 function deselect($id)
02864 {
02865 return $this->controlchoice->deselect($id);
02866 }
02867
02868
02869
02870
02871
02876 function triggerChildActions()
02877 {
02878 $this->controlchoice->triggerChildActions();
02879 }
02880
02885 function &getByValue($value)
02886 {
02887 return $this->controlchoice->getByValue($value);
02888 }
02889
02894 function &get($id)
02895 {
02896 return $this->controlchoice->get($id);
02897 }
02898
02903 function add(&$control)
02904 {
02905 return $this->controlchoice->add($control);
02906 }
02907
02912 function exists($id)
02913 {
02914 return $this->controlchoice->exists($id);
02915 }
02916
02921 function &remove($id)
02922 {
02923 return $this->controlchoice->remove($id);
02924 }
02925
02930 function getAllIds()
02931 {
02932 return $this->controlchoice->getAllIds();
02933 }
02934
02939 function getAllControls()
02940 {
02941 return $this->controlchoice->getAllControls();
02942 }
02943
02948 function getDisplayableIds()
02949 {
02950 return $this->controlchoice->getDisplayableIds();
02951 }
02952
02957 function getDisplayableControls()
02958 {
02959 return $this->controlchoice->getDisplayableControls();
02960 }
02961
02966 function childDisplay($id)
02967 {
02968 return $this->controlchoice->display();
02969 }
02970
02975 function childToString($id)
02976 {
02977 return $this->controlchoice->toString();
02978 }
02979
02984 function childUpdated($id)
02985 {
02986 return $this->controlchoice->updated();
02987 }
02988
02989
02990
02991
02992
02993 function __wakeup()
02994 {
02995 parent::__wakeup();
02996
02997
02998 foreach ($this->node->child_nodes() as $node)
02999 {
03000 $node->unlink_node($node);
03001 }
03002 }
03003
03004 function __sleep()
03005 {
03006 $sleep_vars = parent::__sleep();
03007 array_push($sleep_vars, 'controlchoice');
03008 return $sleep_vars;
03009 }
03010 }
03011
03015 class SingleSelect extends SelectElement
03016 {
03021 function SingleSelect($choice = null)
03022 {
03023 $this->SelectElement($choice);
03024 }
03025 }
03026
03030 class MultipleSelect extends SelectElement
03031 {
03036 function MultipleSelect($choice = null)
03037 {
03038 $this->SelectElement($choice);
03039 $this->setBooleanAttribute('multiple', true);
03040 }
03041
03042
03043
03044
03045
03046 function submitsMultipleValues()
03047 {
03048 return true;
03049 }
03050
03051
03052
03053
03054
03055 function toString()
03056 {
03057 $string = parent::toString();
03058 return $string .= sprintf('<input type="hidden" name="%s" value="">', $this->getControlName());
03059 }
03060 }
03061
03062 ?>