PageRenderTime 79ms CodeModel.GetById 23ms RepoModel.GetById 6ms app.codeStats 0ms

/source/elements/state.js

https://github.com/tornak47/Node-APF
JavaScript | 325 lines | 144 code | 39 blank | 142 comment | 30 complexity | 99670a70278e35c8a3db6cb98570499d MD5 | raw file
  1. /*
  2. * See the NOTICE file distributed with this work for additional
  3. * information regarding copyright ownership.
  4. *
  5. * This is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU Lesser General Public License as
  7. * published by the Free Software Foundation; either version 2.1 of
  8. * the License, or (at your option) any later version.
  9. *
  10. * This software is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * Lesser General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Lesser General Public
  16. * License along with this software; if not, write to the Free
  17. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  18. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  19. *
  20. */
  21. //#ifdef __AMLSTATE || __INC_ALL
  22. /**
  23. * @private
  24. */
  25. apf.StateServer = {
  26. states: {},
  27. groups: {},
  28. locs : {},
  29. removeGroup: function(name, elState){
  30. this.groups[name].remove(elState);
  31. if (!this.groups[name].length) {
  32. if (self[name]) {
  33. self[name].destroy();
  34. self[name] = null;
  35. }
  36. delete this.groups[name];
  37. }
  38. },
  39. addGroup: function(name, elState, pNode){
  40. if (!this.groups[name]) {
  41. this.groups[name] = [];
  42. var pState = new apf.state("state", null, null, true);
  43. pState.parentNode = pNode;
  44. pState.implement(apf.AmlNode);
  45. pState.name = name;
  46. pState.toggle = function(){
  47. for (var next = 0, i = 0; i < apf.StateServer.groups[name].length; i++) {
  48. if (apf.StateServer.groups[name][i].active) {
  49. next = i + 1;
  50. break;
  51. }
  52. }
  53. apf.StateServer.groups[name][
  54. (next == apf.StateServer.groups[name].length) ? 0 : next
  55. ].activate();
  56. }
  57. this.groups[name].pState = self[name] = pState;
  58. }
  59. if (elState)
  60. this.groups[name].push(elState);
  61. return this.groups[name].pState;
  62. },
  63. removeState: function(elState){
  64. delete this.states[elState.name];
  65. },
  66. addState: function(elState){
  67. this.states[elState.name] = elState;
  68. }
  69. }
  70. /**
  71. * Element that specifies a certain state of (a part of) the application. With
  72. * state we mean a collection of properties on objects that have a certain
  73. * value at one time. This element allows you to specify which properties on
  74. * which elements should be set when a state is activated. This element can
  75. * belong to a state-group containing multiple elements with a default state.
  76. * Example:
  77. * This example shows a log in window and four state elements in a state-group.
  78. * <code>
  79. * <a:appsettings>
  80. * <a:auth
  81. * login = "{comm.login(username, password)}"
  82. * logout = "{comm.logout()}"
  83. * autostart = "false"
  84. * window = "winLogin"
  85. * fail-state = "stFail"
  86. * error-state = "stError"
  87. * login-state = "stIdle"
  88. * logout-state = "stLoggedOut"
  89. * waiting-state = "stLoggingIn" />
  90. * </a:appsettings>
  91. * <a:teleport>
  92. * <a:rpc id="comm" protocol="cgi">
  93. * <a:method name="login" url="http://localhost/login.php">
  94. * <a:variable name="username" />
  95. * <a:variable name="password" />
  96. * </a:method>
  97. * <a:method name="logout" url="http://localhost/logout.php" />
  98. * </a:rpc>
  99. * </a:teleport>
  100. *
  101. * <a:state-group
  102. * loginMsg.visible = "false"
  103. * winLogin.disabled = "false">
  104. * <a:state id="stFail"
  105. * loginMsg.value = "Username or password incorrect"
  106. * loginMsg.visible = "true" />
  107. * <a:state id="stError"
  108. * loginMsg.value = "An error has occurred. Please check your network."
  109. * loginMsg.visible = "true" />
  110. * <a:state id="stLoggingIn"
  111. * loginMsg.value = "Please wait whilst logging in..."
  112. * loginMsg.visible = "true"
  113. * winLogin.disabled = "true"
  114. * btnLogout.visible = "false" />
  115. * <a:state id="stIdle"
  116. * btnLogout.visible = "true" />
  117. * <a:state id="stLoggedOut"
  118. * btnLogout.visible = "false"
  119. * loginMsg.visible = "false"
  120. * winLogin.disabled = "false" />
  121. * </a:state-group>
  122. *
  123. * <a:window id="winLogin" visible="true" width="400" height="400">
  124. * <a:label>Username</a:label>
  125. * <a:textbox type="username" value="Lukasz" />
  126. *
  127. * <a:label>Password</a:label>
  128. * <a:textbox type="password" value="ppp" />
  129. *
  130. * <a:label id="loginMsg" />
  131. * <a:button action="login">Log in</a:button>
  132. * </a:window>
  133. * <a:button id="btnLogout" visible="false" action="logout">Log out</a:button>
  134. * </code>
  135. * Example:
  136. * This example shows a label using property binding to get it's caption
  137. * based on the current state.
  138. * <code>
  139. * <a:state group="stRole" id="stUser" caption="You are a user" active="true" />
  140. * <a:state group="stRole" id="stAdmin" caption="You have super powers" />
  141. *
  142. * <a:label value="{stRole.caption}" />
  143. * <a:button onclick="stAdmin.activate()">Become admin</a:button>
  144. * </code>
  145. *
  146. * @event change Fires when the active property of this element changes.
  147. *
  148. * @constructor
  149. * @define state
  150. * @addnode global
  151. *
  152. * @author Ruben Daniels (ruben AT javeline DOT com)
  153. * @version %I%, %G%
  154. * @since 0.9
  155. */
  156. apf.state = function(struct, tagName){
  157. this.$init(tagName || "state", apf.NODE_HIDDEN, struct);
  158. this.$signalElements = [];
  159. this.$groupAdded = {};
  160. this.$locationAdded = '';
  161. };
  162. (function(){
  163. /**** Properties and Attributes ****/
  164. this.$supportedProperties.push("active");
  165. /**
  166. * @attribute {Boolean} active whether this state is the active state
  167. */
  168. this.$propHandlers["active"] = function(value){
  169. //Activate State
  170. if (apf.isTrue(value)) {
  171. if (this.group) {
  172. var nodes = apf.StateServer.groups[this.group];
  173. if (!nodes) {
  174. apf.StateServer.addGroup(this.group, this);
  175. nodes = apf.StateServer.groups[this.group];
  176. }
  177. for (var i = 0; i < nodes.length; i++) {
  178. if (nodes[i] != this && nodes[i].active !== false)
  179. nodes[i].deactivate();
  180. }
  181. }
  182. var q = this.$signalElements;
  183. for (var i = 0; i < q.length; i++) {
  184. if (!self[q[i][0]] || !self[q[i][0]].setProperty) {
  185. //#ifdef __DEBUG
  186. throw new Error(apf.formatErrorString(1013, this,
  187. "Setting State",
  188. "Could not find object to give state: '"
  189. + q[i][0] + "' on property '" + q[i][1] + "'"));
  190. //#endif
  191. continue;
  192. }
  193. self[q[i][0]].setProperty(q[i][1], this[q[i].join(".")]);
  194. }
  195. if (this.group) {
  196. var attr = this.attributes;
  197. for (var i = 0; i < attr.length; i++) {
  198. if (attr[i].nodeName.match(/^on|^(?:group|id)$|^.*\..*$/))
  199. continue;
  200. self[this.group].setProperty(attr[i].nodeName,
  201. attr[i].nodeValue);
  202. }
  203. apf.StateServer.groups[this.group].pState.dispatchEvent("change");
  204. }
  205. this.dispatchEvent("change");
  206. //#ifdef __DEBUG
  207. apf.console.info("Setting state '" + this.name + "' to ACTIVE");
  208. //#endif
  209. }
  210. //Deactivate State
  211. else {
  212. this.setProperty("active", false);
  213. this.dispatchEvent("change");
  214. //#ifdef __DEBUG
  215. apf.console.info("Setting state '" + this.name + "' to INACTIVE");
  216. //#endif
  217. }
  218. };
  219. /**** Public methods ****/
  220. //#ifdef __WITH_CONVENIENCE_API
  221. /**
  222. * Sets the value of this element. This should be one of the values
  223. * specified in the values attribute.
  224. * @param {String} value the new value of this element
  225. */
  226. this.setValue = function(value){
  227. this.active = 9999;
  228. this.setProperty("active", value, false, true);
  229. };
  230. /**
  231. * Actives this state, setting all the properties on the elements that
  232. * were specified.
  233. */
  234. this.activate = function(){
  235. this.active = 9999;
  236. this.setProperty("active", true, false, true);
  237. };
  238. /**
  239. * Deactivates the state of this element. This is mostly a way to let all
  240. * elements that have property bound to this state know it is no longer
  241. * active.
  242. */
  243. this.deactivate = function(){
  244. this.setProperty("active", false, false, true);
  245. };
  246. //#endif
  247. /**** Init ****/
  248. this.$propHandlers["group"] = function(value){
  249. if (value) {
  250. apf.StateServer.addGroup(value, this);
  251. this.$groupAdded = {'value' : value, elState : this};
  252. }
  253. else {
  254. apf.StateServer.removeGroup(this.$groupAdded.value, this.$groupAdded.elState);
  255. this.$groupAdded = {};
  256. }
  257. }
  258. this.$propHandlers["location"] = function(value){
  259. if (value) {
  260. apf.StateServer.locs[value] = this;
  261. this.$locationAdded = value;
  262. }
  263. else {
  264. delete apf.StateServer.locs[this.$locationAdded];
  265. this.$locationAdded = '';
  266. }
  267. }
  268. this.addEventListener("DOMNodeInsertedIntoDocument", function(e){
  269. apf.StateServer.addState(this);
  270. //Properties initialization
  271. var attr = this.attributes;
  272. for (var s, i = 0; i < attr.length; i++) {
  273. s = attr[i].nodeName.split(".");
  274. if (s.length == 2)
  275. this.$signalElements.push(s);
  276. }
  277. });
  278. this.addEventListener("DOMNodeRemovedFromDocument", function(){
  279. this.$signalElements = null;
  280. apf.StateServer.removeState(this);
  281. if (this.group)
  282. apf.StateServer.removeGroup(this.group, this);
  283. });
  284. }).call(apf.state.prototype = new apf.AmlElement());
  285. apf.aml.setElement("state", apf.state);
  286. // #endif