PageRenderTime 47ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/atk4/lib/AbstractView.php

https://github.com/git86/todo
PHP | 325 lines | 175 code | 27 blank | 123 comment | 48 complexity | eade0c09bef7d548f14dc2c843bb70dc MD5 | raw file
Possible License(s): AGPL-3.0
  1. <?php // vim:ts=4:sw=4:et:fdm=marker
  2. /**
  3. * A base class for all Visual objects in Agile Toolkit. The
  4. * important distinctive property of all Views is abiltiy
  5. * to render themselves (produce HTML)
  6. *
  7. * @link http://agiletoolkit.org/learn/understand/view
  8. *//*
  9. ==ATK4===================================================
  10. This file is part of Agile Toolkit 4
  11. http://agiletoolkit.org/
  12. (c) 2008-2011 Romans Malinovskis <atk@agiletech.ie>
  13. Distributed under Affero General Public License v3
  14. See http://agiletoolkit.org/about/license
  15. =====================================================ATK4=*/
  16. abstract class AbstractView extends AbstractObject {
  17. /**
  18. * $template describes how this object is rendered. Template
  19. * can be either string or array or SMlite cloned object.
  20. * For containers, who have sub-elements which render themself
  21. * using SMlite this should be an object.
  22. */
  23. public $template=false;
  24. protected $template_branch=array();
  25. /**
  26. * $template_flush is set to a spot on the template, which
  27. * should be flushed out. When using AJAX we want to show
  28. * only certain region from our template. However several
  29. * childs may want to put their data. This property will
  30. * be set to region's name my call_ajax_render and if it's
  31. * set, call_ajax_render will echo it and return false.
  32. */
  33. public $template_flush=false;
  34. /**
  35. * $spot defines a place on a parent's template where object
  36. * will be inserted
  37. */
  38. public $spot;
  39. protected $controller=null; // View should initialize itself with the help of Controller instance
  40. // {{{ Basic Operations
  41. /** Duplicate view and it's template. Will not duplicate children */
  42. function __clone(){
  43. parent::__clone();
  44. if($this->template)$this->template=clone $this->template;
  45. if($this->controller)$this->controller=clone $this->controller;
  46. }
  47. /** Uses appropriate controller for this view to bind it with model */
  48. function setModel($model,$actual_fields=null){
  49. $c=$this->add('Controller');
  50. if(is_string($model)){
  51. $c->setModel('Model_'.$model);
  52. }else{
  53. $c->setModel($model);
  54. }
  55. if($actual_fields)$c->setActualFields($actual_fields);
  56. $this->setController($c);
  57. return $c;
  58. }
  59. /** Get associated model */
  60. function getModel(){
  61. return $this->getController()->getModel();
  62. }
  63. /** Manually specify controller for view */
  64. function setController($controller){
  65. if(is_object($controller)){
  66. $this->controller=$controller;
  67. $this->controller->owner=$this;
  68. }else{
  69. $this->controller=$this->add($controller);
  70. }
  71. if(method_exists($this->controller,'_bindView'))$this->controller->_bindView();
  72. return $this;
  73. }
  74. function getController(){
  75. return $this->controller;
  76. }
  77. public $_tsBuffer='';
  78. function _tsBuffer($data){
  79. $this->_tsBuffer.=$data;
  80. }
  81. /** Converting View into string will render recursively and produce HTML. Avoid using this. */
  82. function __toString(){
  83. $this->addHook('output',array($this,'_tsBuffer'));
  84. $this->recursiveRender();
  85. $this->removeHook('output',array($this,'_tsBuffer'));
  86. $ret=$this->_tsBuffer;
  87. $this->_tsBuffer='';
  88. return $ret;
  89. }
  90. // }}}
  91. // {{{ Template Setup
  92. /** [private] Called automatically during init */
  93. function initializeTemplate($template_spot=null,$template_branch=null){
  94. if(!$template_spot)$template_spot=$this->defaultSpot();
  95. $this->spot=$template_spot;
  96. if(!isset($template_branch))$template_branch=$this->defaultTemplate();
  97. if(isset($template_branch)){
  98. $this->template_branch=$template_branch;
  99. // template branch would tell us what kind of template we have to use. Let's
  100. // look at several cases
  101. if(is_object($template_branch)){ // it might be already SMlite instance (object)
  102. $this->template=$template_branch; // so we just use that
  103. }else if(is_array($template_branch)){ // it might be array with [0]=template, [1]=tag
  104. if(is_object($template_branch[0])){ // if [0] is object, we'll use that
  105. $this->template=$template_branch[0];
  106. }else{
  107. "loading $template_branch[0]<br />";
  108. $this->template=$this->api->add('SMlite') // or if it's string
  109. ->loadTemplate($template_branch[0]); // we'll use it as a file
  110. }
  111. // Now that we loaded it, let's see which tag we need to cut out
  112. $this->template=$this->template->cloneRegion(isset($template_branch[1])?$template_branch[1]:'_top');
  113. }else{ // brach could be just a string - a region to clone off parent
  114. if(isset($this->owner->template)){
  115. $this->template=$this->owner->template->cloneRegion($template_branch);
  116. }else{
  117. $this->template=$this->add('SMlite');
  118. }
  119. }
  120. $this->template->owner=$this;
  121. }
  122. // Now that the template is loaded, let's take care of parent's template
  123. if($this->owner && (isset($this->owner->template)) && (!empty($this->owner->template))){
  124. $this->owner->template->del($this->spot);
  125. }
  126. // Cool, now let's set _name of this template
  127. if($this->template)$this->template->trySet('_name',$this->name);
  128. $this->initTemplateTags();
  129. }
  130. /** [private] Lets API auto-fill some tags in all views (such as tempalte tag) */
  131. function initTemplateTags(){
  132. if($this->template && $this->api && method_exists($this->api, 'setTags')){
  133. $this->api->setTags($this->template);
  134. }
  135. }
  136. /** Redefine to return default template, when 3rd argument of add() is omitted */
  137. function defaultTemplate(){
  138. return $this->spot;
  139. }
  140. /** Redefine if object needs to output elsewhere, not into Content */
  141. function defaultSpot(){
  142. return 'Content';
  143. }
  144. /** [private] returns actual template branch in same format as defaultTemplate() */
  145. function templateBranch(){
  146. return $this->template_branch;
  147. }
  148. // }}}
  149. // {{{ Rendering, see http://agiletoolkit.org/learn/understand/api/exec
  150. /** [private] Recursively renders all views. Calls render() for all or for the one being cut */
  151. function recursiveRender(){
  152. $cutting_here=false;
  153. if(isset($_GET['cut_object']) && ($_GET['cut_object']==$this->name || $_GET['cut_object']==$this->short_name)){
  154. // If we are cutting here, render childs and then we are done
  155. unset($_GET['cut_object']);
  156. $cutting_here=true;
  157. }
  158. foreach($this->elements as $key=>$obj){
  159. if($obj instanceof AbstractView){
  160. $obj->recursiveRender();
  161. $obj->moveJStoParent();
  162. }
  163. }
  164. if(!isset($_GET['cut_object'])){
  165. if(isset($_GET['cut_region'])){
  166. $this->region_render();
  167. }else{
  168. $this->render();
  169. }
  170. }
  171. if($cutting_here){
  172. $result=$this->owner->template->cloneRegion($this->spot)->render();
  173. if($this->api->jquery)$this->api->jquery->getJS($this);
  174. throw new Exception_StopRender($result);
  175. }
  176. // if template wasn't cut, we move all JS chains to parent
  177. }
  178. /** [private] Append our chains to owner's chains. JS chains bubble up to API or object being cut */
  179. function moveJStoParent(){
  180. $this->owner->js=array_merge_recursive($this->owner->js,$this->js);
  181. }
  182. /** Default render. Redefine if your object needs to dynamically generate data through heavy computation */
  183. function render(){
  184. /**
  185. * For visual objects, their default action while rendering is rely on SMlite engine.
  186. * For sake of simplicity and speed you can redefine this method with a simple call
  187. */
  188. if(!($this->template)){
  189. throw $this->exception("You should specify template for this object")
  190. ->addMoreInfo('object',$this->name);
  191. }
  192. $this->output($this->template->render());
  193. }
  194. /** Call from render where you would use echo. Bypasses template, hence $this->template->set is better */
  195. function output($txt){
  196. if(!$this->hook('output',array($txt))){
  197. if((isset($this->owner->template)) && (!empty($this->owner->template)))
  198. $this->owner->template->append($this->spot,$txt);
  199. }
  200. }
  201. /** When cutting, perform selective render for a region */
  202. function region_render(){
  203. /**
  204. * if GET['ajax'] is set, we need only one chunk of a page
  205. */
  206. if($this->template_flush){
  207. if($this->api->jquery)$this->api->jquery->getJS($this);
  208. throw new Exception_StopRender($this->template->cloneRegion($this->template_flush)->render());
  209. }
  210. $this->render();
  211. if($this->spot==$_GET['cut_region']){
  212. $this->owner->template_flush=$_GET['cut_region'];
  213. }
  214. }
  215. /** When cutting, perform selective render for an object */
  216. function object_render(){
  217. /**
  218. * if GET['cut'] is set, then only particular object will be rendered
  219. */
  220. if($this->name==$_GET['cut_object'] || $this->short_name==$_GET['cut_object']){
  221. $this->downCall('render');
  222. if($this->template)echo $this->template->render();
  223. else $this->render();
  224. return false;
  225. }
  226. }
  227. // }}}
  228. // {{{ Object JavaScript Interface
  229. public $js=array();
  230. /** Creates and binds (if $when) JavaScript chain to object */
  231. function js($when=null,$code=null,$instance=null){
  232. /*
  233. This function is designed for particular object interaction with javascript. We are assuming
  234. that any View object have HTML presence. We are also assuming that it at least adds
  235. one dag to the render tree with ID=$this->name.
  236. First argument $when represents when code must be executed.
  237. * true -> code will be executed immediatelly
  238. * 'click' -> code will be executed if HTML is clicked
  239. * null, false -> code will not be executed, only returned
  240. No matter $when you specify code to execute, function will return JS object, which
  241. can be either chained, but if used as a string, it will return a proper JS code.
  242. 1. Calling with arguments:
  243. $this->js(); // does nothing
  244. $this->js(true,'alert(123)'); // does alert(123) after DOM is ready
  245. 2. When events are used, generated code will use different format
  246. $this->js('click','alert(123)'); // $('#name').click(function(){ alert(123); });
  247. This is very useful when you are trying to make multiple objects interract
  248. $this->js('click',$form->js()->submit());
  249. // $('#name').click(function(){ $('#form').submit(); });
  250. 3. calling js() will return jQuery_Chain object, which you can subsequentally call
  251. to perform multiple actions.
  252. $this->js(true)->parent()->find('.current')->removeClass('current');
  253. // $('#name').parent().find('.current').removeClass('current');
  254. 4. 3rd argument - instance
  255. Sometimes you wish to get back and continue same chain.
  256. $grid->js(null,$this->js()->hide(),'refresh');
  257. In this case - grid might be pre-set non-executable chains for several actions. Example
  258. above will add additional code to that chain which will hide $this element.
  259. See individual component documentation for more information
  260. */
  261. // Create new jQuery_Chain object
  262. if(!isset($this->api->jquery))throw new BaseException("requires jQuery or jUI support");
  263. // Substitute $when to make it better work as a array key
  264. if($when===true)$when='always';
  265. if($when===false || $when===null)$when='never';
  266. if($instance && isset($this->js[$when][$instance])){
  267. $js=$this->js[$when][$instance];
  268. }else{
  269. $js=$this->api->jquery->chain($this);
  270. }
  271. if($code)$js->_prepend($code);
  272. if($instance){
  273. $this->js[$when][$instance]=$js;
  274. }else{
  275. $this->js[$when][]=$js;
  276. }
  277. return $js;
  278. }
  279. /* frame(): Obsolete. ->add('Frame') or Use Controller_Compat_Frame */
  280. /* ajax(): Obsolete. ->js()->univ() or Use Controller_Compat_Frame */
  281. }