Skip to content

Commit

Permalink
- COMPLETELY REWRITTEN AJAX support
Browse files Browse the repository at this point in the history
- new interfaces IRenderable, IPartiallyRenderable
- Presenter::abort() renamed to terminate()
- removed constant Presenter::THIS_VIEW (use string 'this')
- TemplateFilters - added support for new "snippet" mode
  • Loading branch information
dg committed Jul 24, 2008
1 parent be3d795 commit 52fc8a1
Show file tree
Hide file tree
Showing 23 changed files with 415 additions and 206 deletions.
48 changes: 31 additions & 17 deletions Nette/Application/AjaxDriver.php
Expand Up @@ -21,6 +21,9 @@
/*namespace Nette::Application;*/


require_once dirname(__FILE__) . '/../Object.php';



/**
* AJAX output strategy.
Expand All @@ -32,7 +35,7 @@
class AjaxDriver extends /*Nette::*/Object implements IAjaxDriver
{
/** @var array */
private $partials = array();
private $json;



Expand All @@ -48,10 +51,6 @@ public function link($url)



/********************* partial rendering ****************d*g**/



/**
* @return void
*/
Expand All @@ -60,6 +59,11 @@ public function open()
$httpResponse = /*Nette::*/Environment::getHttpResponse();
$httpResponse->setHeader('Content-type: application/x-javascript; charset=utf-8', TRUE);
$httpResponse->expire(FALSE);
$this->json = array(
'nette' => array(
'version' => /*Nette::*/Framework::VERSION,
),
);
}


Expand All @@ -69,23 +73,25 @@ public function open()
*/
public function close()
{
foreach ($this->partials as $id => $content) {
echo "nette.updateHtml(", json_encode($id), ", ", json_encode($content), ");\n";
if ($this->json) {
echo json_encode($this->json);
$this->json = NULL;
}
$this->partials = array();
}



/**
* Updates the partial content.
* Updates the snippet content.
* @param string
* @param string
* @return void
*/
public function addPartial($id, $content)
public function updateSnippet($id, $content)
{
$this->partials[$id] = $content;
if ($this->json) {
$this->json['snippets'][$id] = $content;
}
}


Expand All @@ -95,10 +101,11 @@ public function addPartial($id, $content)
* @param array
* @return void
*/
public function setState($state)
public function updateState($state)
{
$state = http_build_query($state, NULL, '&');
echo "nette.updateState(", json_encode($state), ");\n";
if ($this->json) {
$this->json['state'] = $state;
}
}


Expand All @@ -109,18 +116,25 @@ public function setState($state)
*/
public function redirect($uri)
{
echo "nette.redirect(", json_encode($uri), ");\n";
if ($this->json) {
$this->json['redirect'] = $uri;
}
}



/**
* @param string
* @param mixed
* @return void
*/
public function error($message)
public function fireEvent($event, $arg)
{
echo "nette.error(", json_encode($message), ");\n";
if ($this->json) {
$args = func_get_args();
array_shift($args);
$this->json['events'][] = array('event' => $event, 'args' => $args);
}
}

}
2 changes: 1 addition & 1 deletion Nette/Application/Application.php
Expand Up @@ -189,7 +189,7 @@ public function run()
$request = $e->getRequest();

} catch (AbortException $e) {
// not error, application is correctly aborted
// not error, application is correctly terminated
break;

} catch (Exception $e) {
Expand Down
178 changes: 89 additions & 89 deletions Nette/Application/Control.php
Expand Up @@ -33,25 +33,19 @@
* @copyright Copyright (c) 2004, 2008 David Grudl
* @package Nette::Application
*/
abstract class Control extends PresenterComponent
abstract class Control extends PresenterComponent implements IPartiallyRenderable
{
/** @var string default partial name */
const CONTROL = '_control';

/** @var Nette::Templates::ITemplate */
private $template;

/** @var bool helper for beginPartial() & endPartial() */
private static $invalidBranch = FALSE;

/** @var array see invalidatePartial() & validatePartial() */
private $invalidPartials = array();
/** @var bool helper for beginSnippet() & endSnippet() */
protected static $outputAllowed = TRUE;

/** @var array used by beginPartial() & endPartial() */
private $beginedPartials = array();
/** @var array see invalidateControl() & validateControl() */
private $invalidSnippets = array();

/** @var bool cache for self::hasInvalidChild() */
private $hasInvalidChild = NULL;
/** @var array used by beginSnippet() & endSnippet() */
private static $beginedSnippets = array();



Expand All @@ -66,7 +60,7 @@ final public function getTemplate()
{
if ($this->template === NULL) {
$value = $this->createTemplate();
if (!($value instanceof /*Nette::Templates::*/ITemplate)) {
if (!($value instanceof /*Nette::Templates::*/ITemplate || $value === NULL)) {
$class = get_class($value);
throw new /*::*/UnexpectedValueException("The Nette::Templates::ITemplate object was expected, '$class' was given.");
}
Expand Down Expand Up @@ -97,136 +91,142 @@ protected function createTemplate()


/**
* This component's part must be repainted.
* Forces control or its snippet to repaint.
* @param string
* @return void
*/
public function invalidatePartial($partialName = self::CONTROL)
public function invalidateControl($snippet = NULL, $meta = NULL)
{
$this->invalidPartials[$partialName] = TRUE;
$this->invalidSnippets[$snippet] = (array) $meta;
}



/**
* This component's part should not be repainted.
* Allows control or its snippet to not repaint.
* @param string
* @return void
*/
public function validatePartial($partialName = self::CONTROL)
public function validateControl($snippet = NULL)
{
unset($this->invalidPartials[$partialName]);
if ($snippet === NULL) {
$this->invalidSnippets = array();

} else {
unset($this->invalidSnippets[$snippet]);
}
}



/**
* Must be the component's part repainted?
* @param string
* Is required to repaint the control or its snippet?
* @param string snippet name
* @return bool
*/
public function isPartialInvalid($partialName = self::CONTROL)
public function isControlInvalid($snippet = NULL)
{
if ($partialName === self::CONTROL) {
return count($this->invalidPartials) > 0;
if ($snippet === NULL) {
if (count($this->invalidSnippets) > 0) {
return TRUE;

} else {
foreach ($this->getComponents() as $component) {
if ($component instanceof IRenderable && $component->isControlInvalid()) {
// $this->invalidSnippets['__child'] = TRUE; // as cache
return TRUE;
}
}
}

} else {
return isset($this->invalidPartials[$partialName]);
return isset($this->invalidSnippets[NULL]) || isset($this->invalidSnippets[$snippet]);
}
}



/**
* Starts conditional partial rendering. Returns TRUE if partial was started.
* @param string partial name
* @param string start element
*
* @return bool
*/
public function beginPartial($name = self::CONTROL, $mask = '<div %id>')
public function isOutputAllowed()
{
if (isset($this->beginedPartials[$name])) {
throw new /*::*/InvalidStateException("Partial '$name' has been already started.");
}
return self::$outputAllowed;
}



/**
*
* @return bool
*/
public function getSnippetId($name = 'main')
{
// HTML 4 ID & NAME: [A-Za-z][A-Za-z0-9:_.-]*
$id = $this->getUniqueId() . ':' . $name; // TODO: append counter
return $this->getUniqueId() . ':' . $name;
}



if ($this->getPresenter()->isPartialMode()) {
if (self::$invalidBranch) {
$this->beginedPartials[$name] = array($id, NULL);
/**
* Starts conditional snippet rendering. Returns TRUE if snippet was started.
* @param string snippet name
* @param string start element
* @return bool
*/
public function beginSnippet($name = 'main', $startTag = 'div')
{
$id = $this->getSnippetId($name);

} elseif (isset($this->invalidPartials[self::CONTROL]) || isset($this->invalidPartials[$name])) {
self::$invalidBranch = TRUE;
ob_start();
$this->beginedPartials[$name] = array($id, ob_get_level());
if (self::$outputAllowed) {
$startTag = trim($startTag, '<>');
self::$beginedSnippets[] = array($id, NULL, $startTag);
echo '<', $startTag, ' id="', $id, '">';

} elseif ($this->hasInvalidChild()) {
$this->beginedPartials[$name] = array($id, NULL);
} elseif (isset($this->invalidSnippets[$name])) {
self::$outputAllowed = TRUE;
ob_start();
self::$beginedSnippets[] = array($id, ob_get_level(), $this->invalidSnippets[$name]);

} else {
return FALSE;
}
} elseif (isset($this->invalidSnippets[NULL])) {
self::$outputAllowed = TRUE;
ob_start();
self::$beginedSnippets[] = array($id, ob_get_level(), NULL);

} else {
$this->beginedPartials[$name] = $id;
echo str_replace('%id', 'id="' . $id . '"', $mask);
return FALSE;
}

return TRUE;
}



/**
* Finist conditional partial rendering.
* @param string partial name
* @param string end element
* Finist conditional snippet rendering.
* @param string snippet name
* @return void
*/
public function endPartial($name = self::CONTROL, $mask = '</div>')
public function endSnippet($name = NULL)
{
if (!isset($this->beginedPartials[$name])) {
throw new /*::*/InvalidStateException("Partial '$name' has not been started.");
list($id, $level, $endTag) = array_pop(self::$beginedSnippets);

if ($name != NULL && $id !== ($this->getUniqueId() . ':' . $name)) {
throw new /*::*/InvalidStateException("Snippet '$name' cannot be ended here.");
}

if ($this->getPresenter()->isPartialMode()) {
list($id, $level) = $this->beginedPartials[$name];
if ($level !== NULL) {
if ($level !== ob_get_level()) {
throw new /*::*/InvalidStateException("Partial '$name' cannot be ended here.");
}
$this->getPresenter()->getAjaxDriver()->addPartial($id, ob_get_flush());
self::$invalidBranch = FALSE;
if ($level !== NULL) {
if ($level !== ob_get_level()) {
throw new /*::*/InvalidStateException("Snippet '$name' cannot be ended here.");
}
$this->getPresenter()->getAjaxDriver()->updateSnippet($id, ob_get_clean(), $endTag);
self::$outputAllowed = FALSE;

} else {
echo str_replace('%id', 'id="' . $this->beginedPartials[$name] . '"', $mask);
} elseif (self::$outputAllowed) {
echo '</', $endTag, '>';
}

unset($this->beginedPartials[$name]);
}



/**
* Lookup for invalid children.
* @return bool
*/
private function hasInvalidChild()
{
if ($this->hasInvalidChild === NULL) {
if (count($this->invalidPartials) > 0) {
return $this->hasInvalidChild = TRUE;
}
foreach ($this->getComponents() as $component) {
if ($component instanceof Control) {
if ($component->hasInvalidChild()) {
return $this->hasInvalidChild = TRUE;
}
}
}
}
return $this->hasInvalidChild = FALSE;
unset(self::$beginedSnippets[$id]);
}

}

0 comments on commit 52fc8a1

Please sign in to comment.