1: <?php
2: /**
3: * The QJsTimer class (and related classes) reside here.
4: *
5: * This control does not produce any visible element. It only launches timer based events on the client side.
6: * Javascript is generated by QCubed.
7: *
8: * @package Controls
9: * @filesource
10: */
11:
12: /**
13: * Use this event with the QJsTimer control
14: * this event is trigger after a
15: * delay specified in QJsTimer (param DeltaTime)
16: */
17: class QTimerExpiredEvent extends QEvent {
18: /** Event's name. Used by QCubed framework for its internal purpose */
19: const EventName = 'timerexpiredevent';
20: }
21:
22:
23: /**
24: * Timer Control:
25: * This control uses a javascript timer to execute Actions after a defined time
26: * Periodic or one shot timers are possible.
27: * You can add only one type of Event to to this control: QTimerExpiredEvent
28: * but multiple actions can be registered for this event
29: * @property int $DeltaTime Time till the timer fires and executes the Actions added.
30: * @property boolean $Periodic <ul>
31: * <li><strong>true</strong>: timer is restarted after firing</li>
32: * <li><strong>false</strong>: you have to restart the timer by calling Start()</li>
33: * </ul>
34: *
35: * @property boolean $Started <strong>true</strong>: timer is running / <strong>false</strong>: stopped
36: * @property boolean $RestartOnServerAction After a 'Server Action' (QServerAction) the executed java script
37: * (including the timer) is stopped!
38: * Set this parameter to true to restart the timer automatically.
39: * @notes <ul><li>You do not need to render this control!</li>
40: * <li>QTimerExpiredEvent - condition and delay parameters of the constructor are ignored (for now) </li>
41: */
42: class QJsTimerBase extends QControl {
43: // Values determining the state of the timer
44: /** Constant used to indicate that the timer has stopped */
45: const Stopped = 0;
46: /** Constant used to indicate that the timer has started */
47: const Started = 1;
48: /** Constant used to indicate that the timer has autostart enabled (starts with the page load) */
49: const AutoStart = 2;
50:
51: /** @var bool does the timer run periodically once started? */
52: protected $blnPeriodic = true;
53: /** @var int The duration after which the timer will fire (in milliseconds) */
54: protected $intDeltaTime = 0;
55: /** @var int default state in which timer will be (stopped) */
56: protected $intState = QJsTimer::Stopped;
57: /** @var bool should the timer start after a QServerAction occurrs. */
58: protected $blnRestartOnServerAction = false;
59:
60:
61: /**
62: * @param QForm|QControl $objParentObject the form or parent control
63: * @param int $intTime timer interval in ms
64: * @param boolean $blnPeriodic if true the timer is "restarted" automatically after it has fired
65: * @param boolean $blnStartNow starts the timer automatically after adding the first action
66: * @param string $strTimerId
67: *
68: * @throws QCallerException
69: * @return QJsTimer
70: */
71: public function __construct($objParentObject, $intTime = 0, $blnPeriodic = true, $blnStartNow = true, $strTimerId = null) {
72: try {
73: parent::__construct($objParentObject, $strTimerId);
74: } catch (QCallerException $objExc) {
75: $objExc->IncrementOffset();
76: throw $objExc;
77: }
78:
79: $this->intDeltaTime = $intTime;
80: $this->blnPeriodic = $blnPeriodic;
81: if ($intTime != QJsTimer::Stopped && $blnStartNow) {
82: $this->intState = QJsTimer::AutoStart;
83: } //prepare to start the timer after the first action gets added
84: }
85:
86: /**
87: * Returns the callback string
88: * @return string
89: */
90: private function callbackString() {
91: return "qcubed._objTimers['" . $this->strControlId . "_cb']";
92: }
93:
94: /**
95: * Returns a timer ID (string) as an element of the 'qcubed._objTimers' javascript array.
96: * This array is used to start and stop timers (and keep track)
97: * @return string
98: */
99: private function tidString() {
100: return "qcubed._objTimers['" . $this->strControlId . "_tId']";
101: }
102:
103: /**
104: * @param int $intTime (optional)
105: * sets the interval/delay, after that the timer executes the registered actions
106: * if no parameter is given the time stored in $intDeltaTime is used
107: * @throws QCallerException
108: * @return void
109: */
110: public function Start($intTime = null) {
111: $this->Stop();
112: if ($intTime != null && is_int($intTime)) {
113: $this->intDeltaTime = $intTime;
114: }
115: $event = $this->getEvent();
116: if (!$event) {
117: throw new QCallerException("Can't start the timer: add an Event/Action first!");
118: }
119:
120: // Is the timer periodic or runs only once?
121: if ($this->blnPeriodic) {
122: // timer is periodic. We will set the interval
123: $strJS = $this->tidString() . ' = window.setInterval("' . $this->callbackString() . '()", ' . $this->intDeltaTime . ');';
124: } else {
125: // timer is not periodic. We will set the timeout
126: $strJS = $this->tidString() . ' = window.setTimeout("' . $this->callbackString() . '()", ' . $this->intDeltaTime . ');';
127: }
128: QApplication::ExecuteJavaScript($strJS);
129: $this->intState = QJsTimer::Started;
130: }
131:
132: /**
133: * stops the timer
134: */
135: public function Stop() {
136: $event = $this->getEvent();
137: if (!$event) {
138: throw new QCallerException('Can\'t stop the timer: no Event/Action present!');
139: }
140: // Is timer periodic or one-time?
141: if ($this->blnPeriodic) {
142: // Periodic timer. We should clear the interval we had set beforehand
143: $strJS = 'window.clearInterval(' . $this->tidString() . ');';
144: } else {
145: // One-time timer. We should clear the timeout we had set beforehand
146: $strJS = 'window.clearTimeout(' . $this->tidString() . ');';
147: }
148: QApplication::ExecuteJavaScript($strJS);
149: $this->intState = QJsTimer::Stopped;
150: }
151:
152: /**
153: * Adds an action to the control
154: *
155: * @param QEvent $objEvent has to be an instance of QTimerExpiredEvent
156: * @param QAction $objAction Only a QTimerExpiredEvent can be added,
157: * but multiple Actions using the same event are possible!
158: *
159: * @throws QCallerException
160: * @return void
161: */
162: public function AddAction($objEvent, $objAction) {
163: if (!($objEvent instanceof QTimerExpiredEvent)) {
164: throw new QCallerException('First parameter of QJsTimer::AddAction is expecting an object of type QTimerExpiredEvent');
165: }
166: if (!($objAction instanceof QAction)) {
167: throw new QCallerException('Second parameter of AddAction is expecting an object of type QAction');
168: }
169:
170: $strEventName = $objEvent->EventName;
171: if (!count($this->objActionArray)) {
172: //no event registerd yet
173: $this->objActionArray[$strEventName] = array();
174: }
175:
176: // Store the Event object in the Action object
177: $objAction->Event = $objEvent;
178:
179: array_push($this->objActionArray[$strEventName], $objAction);
180:
181: if ($this->intState === QJsTimer::AutoStart && $this->intDeltaTime != 0) {
182: $this->Start();
183: } //autostart the timer
184:
185: $this->blnModified = true;
186:
187: }
188:
189: /**
190: * Returns all actions connected/attached to the timer
191: * @param string $strEventType
192: * @param null $strActionType
193: *
194: * @return array
195: */
196: public function GetAllActions($strEventType, $strActionType = null) {
197: if (($strEventType == 'QTimerExpiredEvent' && $this->blnPeriodic == false) &&
198: (($strActionType == 'QAjaxAction' && $this->objForm->CallType == QCallType::Ajax) ||
199: ($strActionType == 'QServerAction' && $this->objForm->CallType == QCallType::Server))
200: ) {
201: //if we are in an ajax or server post and our timer is not periodic
202: //and this method gets called then the timer has finished(stopped) --> set the State flag to "stopped"
203: $this->intState = QJsTimer::Stopped;
204: }
205: return parent::GetAllActions($strEventType, $strActionType);
206: }
207:
208: /**
209: * Remove all actions attached to the timer
210: * @param null $strEventName
211: */
212: public function RemoveAllActions($strEventName = null) {
213: $this->Stop(); //no actions are registered for this timer stop it
214: parent::RemoveAllActions($strEventName);
215: }
216:
217: /**
218: * @return null|
219: */
220: public function GetEvent() {
221: if (!count($this->objActionArray)) {
222: return null;
223: }
224: // point to the first action in the list
225: $arrActions = reset($this->objActionArray);
226: return reset($arrActions)->Event;
227: }
228:
229: /**
230: * Returns all action attributes
231: * @return string
232: */
233: public function RenderActionScripts() {
234: $strToReturn = $this->callbackString() . " = ";
235: if (!count($this->objActionArray)) {
236: return $strToReturn . 'null;';
237: }
238:
239: $strToReturn .= 'function() {';
240:
241: foreach (reset($this->objActionArray) as $objAction) {
242: /** @var QAction $objAction */
243: $strToReturn .= ' ' . $objAction->RenderScript($this);
244: }
245: if ($this->ActionsMustTerminate) {
246: if (QApplication::IsBrowser(QBrowserType::InternetExplorer_6_0)) {
247: $strToReturn .= ' qc.terminateEvent(event);';
248: } else {
249: $strToReturn .= ' return false;';
250: }
251: }
252: $strToReturn .= ' }; ';
253: return $strToReturn;
254: }
255:
256: /**
257: * Returns all Javscript that needs to be executed after rendering of this control
258: * (It overrides the GetEndScript of the parent to handle specific case of QJsTimers)
259: * @return string
260: */
261: public function GetEndScript() {
262: if ($this->objForm->CallType == QCallType::Server) {
263: //this point is not reached on initial rendering
264: if ($this->blnRestartOnServerAction && $this->intState === QJsTimer::Started) {
265: $this->Start();
266: } //restart after a server action
267: else {
268: $this->intState = QJsTimer::Stopped;
269: }
270: }
271: return parent::GetEndScript();
272: }
273:
274: /**
275: * PHP magic function to get value of properties of an object of this class
276: * @param string $strName Name of the properties
277: *
278: * @return array|bool|int|mixed|null|QControl|QForm|string
279: * @throws QCallerException
280: */
281: public function __get($strName) {
282: switch ($strName) {
283: case 'DeltaTime':
284: return $this->intDeltaTime;
285: case 'Periodic':
286: return $this->blnPeriodic;
287: case 'Started':
288: return ($this->intState === QJsTimer::Started);
289: case 'RestartOnServerAction':
290: return $this->blnRestartOnServerAction;
291: case 'Rendered':
292: return true;
293: default:
294: try {
295: return parent::__get($strName);
296: } catch (QCallerException $objExc) {
297: $objExc->IncrementOffset();
298: throw $objExc;
299: }
300: }
301: }
302:
303: /**
304: * PHP Magic function to set property values for an object of this class
305: * @param string $strName Name of the property
306: * @param string $mixValue Value of the property
307: *
308: * @return mixed
309: * @throws QCallerException
310: * @throws QInvalidCastException
311: */
312: public function __set($strName, $mixValue) {
313: switch ($strName) {
314: case "DeltaTime":
315: try {
316: $this->intDeltaTime = QType::Cast($mixValue, QType::Integer);
317: break;
318: } catch (QInvalidCastException $objExc) {
319: $objExc->IncrementOffset();
320: throw $objExc;
321: }
322: break;
323: case 'Periodic':
324: try {
325: $newMode = QType::Cast($mixValue, QType::Boolean);
326: if ($this->blnPeriodic != $newMode) {
327: if ($this->intState === QJsTimer::Started) {
328: $this->Stop();
329: $this->blnPeriodic = $newMode;
330: $this->Start();
331: } else {
332: $this->blnPeriodic = $newMode;
333: }
334: }
335: } catch (QInvalidCastException $objExc) {
336: $objExc->IncrementOffset();
337: throw $objExc;
338: }
339: break;
340: case 'RestartOnServerAction':
341: try {
342: $this->blnRestartOnServerAction = QType::Cast($mixValue, QType::Boolean);
343: } catch (QInvalidCastException $objExc) {
344: $objExc->IncrementOffset();
345: throw $objExc;
346: }
347: break;
348: case "Rendered": //ensure that the control is marked as Rendered to get js updates
349: $this->blnRendered = true;
350: break;
351: default:
352: try {
353: parent::__set($strName, $mixValue);
354: } catch (QCallerException $objExc) {
355: $objExc->IncrementOffset();
356: throw $objExc;
357: }
358: break;
359: }
360: }
361:
362: /**
363: * Render function for the Control (must not be called becasue QJsTimer is not for being rendered)
364: * @param bool $blnDisplayOutput useless in this case
365: *
366: * @return string|void
367: * @throws QCallerException
368: */
369: public function Render($blnDisplayOutput = true) {
370: throw new QCallerException('Do not render QJsTimer!');
371: }
372:
373: /**
374: * Add a child control to the current control (useless because QJsTimer cannot have children)
375: * @param QControl $objControl
376: *
377: * @throws QCallerException
378: */
379: public function AddChildControl(QControl $objControl) {
380: throw new QCallerException('Do not add child-controls to an instance of QJsTimer!');
381: }
382:
383: /**
384: * Remove the child controls (useless)
385: * Since QJsTimer cannot have children, removing child controls does not yeild anything
386: * @param string $strControlId
387: * @param bool $blnRemoveFromForm
388: */
389: public function RemoveChildControl($strControlId, $blnRemoveFromForm) {
390: }
391:
392: /**
393: * Get the HTML for the control (blank in this case becuase QJsTimer cannot be rendered)
394: * @return string
395: */
396: protected function GetControlHtml() {
397: // no control html
398: return "";
399: }
400:
401: /**
402: * This function would typically parse the data posted back by the control.
403: */
404: public function ParsePostData() {
405: }
406:
407: /**
408: * Validation logic for control. Since we never render, we must return true to continue using the control.
409: * @return bool
410: */
411: public function Validate() {
412: return true;
413: }
414: }