odac 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/.editorconfig +21 -0
  2. package/.github/workflows/auto-pr-description.yml +49 -0
  3. package/.github/workflows/release.yml +32 -0
  4. package/.github/workflows/test-coverage.yml +58 -0
  5. package/.husky/pre-commit +2 -0
  6. package/.kiro/steering/code-style.md +56 -0
  7. package/.kiro/steering/product.md +20 -0
  8. package/.kiro/steering/structure.md +77 -0
  9. package/.kiro/steering/tech.md +87 -0
  10. package/.prettierrc +10 -0
  11. package/.releaserc.js +134 -0
  12. package/AGENTS.md +84 -0
  13. package/CHANGELOG.md +181 -0
  14. package/CODE_OF_CONDUCT.md +83 -0
  15. package/CONTRIBUTING.md +63 -0
  16. package/LICENSE +661 -0
  17. package/README.md +57 -0
  18. package/SECURITY.md +26 -0
  19. package/bin/candy +10 -0
  20. package/bin/candypack +10 -0
  21. package/cli/index.js +3 -0
  22. package/cli/src/Cli.js +348 -0
  23. package/cli/src/Connector.js +93 -0
  24. package/cli/src/Monitor.js +416 -0
  25. package/core/Candy.js +87 -0
  26. package/core/Commands.js +239 -0
  27. package/core/Config.js +1094 -0
  28. package/core/Lang.js +52 -0
  29. package/core/Log.js +43 -0
  30. package/core/Process.js +26 -0
  31. package/docs/backend/01-overview/01-whats-in-the-candy-box.md +9 -0
  32. package/docs/backend/01-overview/02-super-handy-helper-functions.md +9 -0
  33. package/docs/backend/01-overview/03-development-server.md +79 -0
  34. package/docs/backend/02-structure/01-typical-project-layout.md +39 -0
  35. package/docs/backend/03-config/00-configuration-overview.md +214 -0
  36. package/docs/backend/03-config/01-database-connection.md +60 -0
  37. package/docs/backend/03-config/02-static-route-mapping-optional.md +20 -0
  38. package/docs/backend/03-config/03-request-timeout.md +11 -0
  39. package/docs/backend/03-config/04-environment-variables.md +227 -0
  40. package/docs/backend/03-config/05-early-hints.md +352 -0
  41. package/docs/backend/04-routing/01-basic-page-routes.md +28 -0
  42. package/docs/backend/04-routing/02-controller-less-view-routes.md +43 -0
  43. package/docs/backend/04-routing/03-api-and-data-routes.md +20 -0
  44. package/docs/backend/04-routing/04-authentication-aware-routes.md +48 -0
  45. package/docs/backend/04-routing/05-advanced-routing.md +14 -0
  46. package/docs/backend/04-routing/06-error-pages.md +101 -0
  47. package/docs/backend/04-routing/07-cron-jobs.md +149 -0
  48. package/docs/backend/05-controllers/01-how-to-build-a-controller.md +17 -0
  49. package/docs/backend/05-controllers/02-your-trusty-candy-assistant.md +20 -0
  50. package/docs/backend/05-controllers/03-controller-classes.md +93 -0
  51. package/docs/backend/05-forms/01-custom-forms.md +395 -0
  52. package/docs/backend/05-forms/02-automatic-database-insert.md +297 -0
  53. package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +96 -0
  54. package/docs/backend/06-request-and-response/02-sending-a-response-replying-to-the-user.md +40 -0
  55. package/docs/backend/07-views/01-the-view-directory.md +73 -0
  56. package/docs/backend/07-views/02-rendering-a-view.md +179 -0
  57. package/docs/backend/07-views/03-template-syntax.md +181 -0
  58. package/docs/backend/07-views/03-variables.md +328 -0
  59. package/docs/backend/07-views/04-request-data.md +231 -0
  60. package/docs/backend/07-views/05-conditionals.md +290 -0
  61. package/docs/backend/07-views/06-loops.md +353 -0
  62. package/docs/backend/07-views/07-translations.md +358 -0
  63. package/docs/backend/07-views/08-backend-javascript.md +398 -0
  64. package/docs/backend/07-views/09-comments.md +297 -0
  65. package/docs/backend/08-database/01-database-connection.md +99 -0
  66. package/docs/backend/08-database/02-using-mysql.md +322 -0
  67. package/docs/backend/09-validation/01-the-validator-service.md +424 -0
  68. package/docs/backend/10-authentication/01-user-logins-with-authjs.md +53 -0
  69. package/docs/backend/10-authentication/02-foiling-villains-with-csrf-protection.md +55 -0
  70. package/docs/backend/10-authentication/03-register.md +134 -0
  71. package/docs/backend/10-authentication/04-candy-register-forms.md +676 -0
  72. package/docs/backend/10-authentication/05-session-management.md +159 -0
  73. package/docs/backend/10-authentication/06-candy-login-forms.md +596 -0
  74. package/docs/backend/11-mail/01-the-mail-service.md +42 -0
  75. package/docs/backend/12-streaming/01-streaming-overview.md +300 -0
  76. package/docs/backend/13-utilities/01-candy-var.md +504 -0
  77. package/docs/frontend/01-overview/01-introduction.md +146 -0
  78. package/docs/frontend/02-ajax-navigation/01-quick-start.md +608 -0
  79. package/docs/frontend/02-ajax-navigation/02-configuration.md +370 -0
  80. package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +519 -0
  81. package/docs/frontend/03-forms/01-form-handling.md +420 -0
  82. package/docs/frontend/04-api-requests/01-get-post.md +443 -0
  83. package/docs/frontend/05-streaming/01-client-streaming.md +163 -0
  84. package/docs/index.json +452 -0
  85. package/docs/server/01-installation/01-quick-install.md +19 -0
  86. package/docs/server/01-installation/02-manual-installation-via-npm.md +9 -0
  87. package/docs/server/02-get-started/01-core-concepts.md +7 -0
  88. package/docs/server/02-get-started/02-basic-commands.md +57 -0
  89. package/docs/server/02-get-started/03-cli-reference.md +276 -0
  90. package/docs/server/02-get-started/04-cli-quick-reference.md +102 -0
  91. package/docs/server/03-service/01-start-a-new-service.md +57 -0
  92. package/docs/server/03-service/02-delete-a-service.md +48 -0
  93. package/docs/server/04-web/01-create-a-website.md +36 -0
  94. package/docs/server/04-web/02-list-websites.md +9 -0
  95. package/docs/server/04-web/03-delete-a-website.md +29 -0
  96. package/docs/server/05-subdomain/01-create-a-subdomain.md +32 -0
  97. package/docs/server/05-subdomain/02-list-subdomains.md +33 -0
  98. package/docs/server/05-subdomain/03-delete-a-subdomain.md +41 -0
  99. package/docs/server/06-ssl/01-renew-an-ssl-certificate.md +34 -0
  100. package/docs/server/07-mail/01-create-a-mail-account.md +23 -0
  101. package/docs/server/07-mail/02-delete-a-mail-account.md +20 -0
  102. package/docs/server/07-mail/03-list-mail-accounts.md +20 -0
  103. package/docs/server/07-mail/04-change-account-password.md +23 -0
  104. package/eslint.config.mjs +120 -0
  105. package/framework/index.js +4 -0
  106. package/framework/src/Auth.js +309 -0
  107. package/framework/src/Candy.js +81 -0
  108. package/framework/src/Config.js +79 -0
  109. package/framework/src/Env.js +60 -0
  110. package/framework/src/Lang.js +57 -0
  111. package/framework/src/Mail.js +83 -0
  112. package/framework/src/Mysql.js +575 -0
  113. package/framework/src/Request.js +301 -0
  114. package/framework/src/Route/Cron.js +128 -0
  115. package/framework/src/Route/Internal.js +439 -0
  116. package/framework/src/Route.js +455 -0
  117. package/framework/src/Server.js +15 -0
  118. package/framework/src/Stream.js +163 -0
  119. package/framework/src/Token.js +37 -0
  120. package/framework/src/Validator.js +271 -0
  121. package/framework/src/Var.js +211 -0
  122. package/framework/src/View/EarlyHints.js +190 -0
  123. package/framework/src/View/Form.js +600 -0
  124. package/framework/src/View.js +513 -0
  125. package/framework/web/candy.js +838 -0
  126. package/jest.config.js +22 -0
  127. package/locale/de-DE.json +80 -0
  128. package/locale/en-US.json +79 -0
  129. package/locale/es-ES.json +80 -0
  130. package/locale/fr-FR.json +80 -0
  131. package/locale/pt-BR.json +80 -0
  132. package/locale/ru-RU.json +80 -0
  133. package/locale/tr-TR.json +85 -0
  134. package/locale/zh-CN.json +80 -0
  135. package/package.json +86 -0
  136. package/server/index.js +5 -0
  137. package/server/src/Api.js +88 -0
  138. package/server/src/DNS.js +940 -0
  139. package/server/src/Hub.js +535 -0
  140. package/server/src/Mail.js +571 -0
  141. package/server/src/SSL.js +180 -0
  142. package/server/src/Server.js +27 -0
  143. package/server/src/Service.js +248 -0
  144. package/server/src/Subdomain.js +64 -0
  145. package/server/src/Web/Firewall.js +170 -0
  146. package/server/src/Web/Proxy.js +134 -0
  147. package/server/src/Web.js +451 -0
  148. package/server/src/mail/imap.js +1091 -0
  149. package/server/src/mail/server.js +32 -0
  150. package/server/src/mail/smtp.js +786 -0
  151. package/test/cli/Cli.test.js +36 -0
  152. package/test/core/Candy.test.js +234 -0
  153. package/test/core/Commands.test.js +538 -0
  154. package/test/core/Config.test.js +1435 -0
  155. package/test/core/Lang.test.js +250 -0
  156. package/test/core/Process.test.js +156 -0
  157. package/test/framework/Route.test.js +239 -0
  158. package/test/framework/View/EarlyHints.test.js +282 -0
  159. package/test/scripts/check-coverage.js +132 -0
  160. package/test/server/Api.test.js +647 -0
  161. package/test/server/Client.test.js +338 -0
  162. package/test/server/DNS.test.js +2050 -0
  163. package/test/server/DNS.test.js.bak +2084 -0
  164. package/test/server/Log.test.js +73 -0
  165. package/test/server/Mail.account.test_.js +460 -0
  166. package/test/server/Mail.init.test_.js +411 -0
  167. package/test/server/Mail.test_.js +1340 -0
  168. package/test/server/SSL.test_.js +1491 -0
  169. package/test/server/Server.test.js +765 -0
  170. package/test/server/Service.test_.js +1127 -0
  171. package/test/server/Subdomain.test.js +440 -0
  172. package/test/server/Web/Firewall.test.js +175 -0
  173. package/test/server/Web.test_.js +1562 -0
  174. package/test/server/__mocks__/acme-client.js +17 -0
  175. package/test/server/__mocks__/bcrypt.js +50 -0
  176. package/test/server/__mocks__/child_process.js +389 -0
  177. package/test/server/__mocks__/crypto.js +432 -0
  178. package/test/server/__mocks__/fs.js +450 -0
  179. package/test/server/__mocks__/globalCandy.js +227 -0
  180. package/test/server/__mocks__/http-proxy.js +105 -0
  181. package/test/server/__mocks__/http.js +575 -0
  182. package/test/server/__mocks__/https.js +272 -0
  183. package/test/server/__mocks__/index.js +249 -0
  184. package/test/server/__mocks__/mail/server.js +100 -0
  185. package/test/server/__mocks__/mail/smtp.js +31 -0
  186. package/test/server/__mocks__/mailparser.js +81 -0
  187. package/test/server/__mocks__/net.js +369 -0
  188. package/test/server/__mocks__/node-forge.js +328 -0
  189. package/test/server/__mocks__/os.js +320 -0
  190. package/test/server/__mocks__/path.js +291 -0
  191. package/test/server/__mocks__/selfsigned.js +8 -0
  192. package/test/server/__mocks__/server/src/mail/server.js +100 -0
  193. package/test/server/__mocks__/server/src/mail/smtp.js +31 -0
  194. package/test/server/__mocks__/smtp-server.js +106 -0
  195. package/test/server/__mocks__/sqlite3.js +394 -0
  196. package/test/server/__mocks__/testFactories.js +299 -0
  197. package/test/server/__mocks__/testHelpers.js +363 -0
  198. package/test/server/__mocks__/tls.js +229 -0
  199. package/watchdog/index.js +3 -0
  200. package/watchdog/src/Watchdog.js +156 -0
  201. package/web/config.json +5 -0
  202. package/web/controller/page/about.js +27 -0
  203. package/web/controller/page/index.js +34 -0
  204. package/web/package.json +18 -0
  205. package/web/public/assets/css/style.css +1835 -0
  206. package/web/public/assets/js/app.js +96 -0
  207. package/web/route/www.js +19 -0
  208. package/web/skeleton/main.html +22 -0
  209. package/web/view/content/about.html +65 -0
  210. package/web/view/content/home.html +205 -0
  211. package/web/view/footer/main.html +11 -0
  212. package/web/view/head/main.html +5 -0
  213. package/web/view/header/main.html +14 -0
@@ -0,0 +1,290 @@
1
+ ## 🔀 Conditional Rendering
2
+
3
+ Conditionals allow you to show or hide content based on conditions. This is essential for dynamic user interfaces.
4
+
5
+ ### Basic If Statement
6
+
7
+ ```html
8
+ <candy:if condition="user.isAdmin">
9
+ <p>Welcome to the admin panel!</p>
10
+ </candy:if>
11
+ ```
12
+
13
+ The content inside the tag is only rendered if the condition is true.
14
+
15
+ ### If-Else Structure
16
+
17
+ ```html
18
+ <candy:if condition="user.isLoggedIn">
19
+ <p>Welcome back, <candy var="user.name" />!</p>
20
+ <candy:else>
21
+ <p>Please log in to continue.</p>
22
+ </candy:if>
23
+ ```
24
+
25
+ ### If-ElseIf-Else Structure
26
+
27
+ ```html
28
+ <candy:if condition="user.role === 'admin'">
29
+ <div class="admin-panel">
30
+ <p>You have full admin privileges</p>
31
+ </div>
32
+ <candy:elseif condition="user.role === 'moderator'">
33
+ <div class="moderator-panel">
34
+ <p>You have moderator privileges</p>
35
+ </div>
36
+ <candy:elseif condition="user.role === 'editor'">
37
+ <div class="editor-panel">
38
+ <p>You have editor privileges</p>
39
+ </div>
40
+ <candy:else>
41
+ <div class="user-panel">
42
+ <p>You have regular user privileges</p>
43
+ </div>
44
+ </candy:if>
45
+ ```
46
+
47
+ ### Condition Syntax
48
+
49
+ Conditions use standard JavaScript expressions:
50
+
51
+ ```html
52
+ <!-- Equality -->
53
+ <candy:if condition="status === 'active'">Active</candy:if>
54
+
55
+ <!-- Comparison -->
56
+ <candy:if condition="age >= 18">Adult</candy:if>
57
+ <candy:if condition="price < 100">Affordable</candy:if>
58
+
59
+ <!-- Logical operators -->
60
+ <candy:if condition="user.isVerified && user.isPremium">
61
+ Premium Verified User
62
+ </candy:if>
63
+
64
+ <candy:if condition="role === 'admin' || role === 'moderator'">
65
+ Staff Member
66
+ </candy:if>
67
+
68
+ <!-- Negation -->
69
+ <candy:if condition="!user.isBanned">
70
+ Welcome!
71
+ </candy:if>
72
+
73
+ <!-- Existence check -->
74
+ <candy:if condition="user">
75
+ User exists
76
+ </candy:if>
77
+
78
+ <!-- Array/String length -->
79
+ <candy:if condition="items.length > 0">
80
+ You have items
81
+ </candy:if>
82
+
83
+ <!-- Method calls -->
84
+ <candy:if condition="Candy.Auth.check()">
85
+ Logged in
86
+ </candy:if>
87
+ ```
88
+
89
+ ### Practical Examples
90
+
91
+ #### User Authentication Status
92
+
93
+ ```html
94
+ <nav>
95
+ <candy:if condition="Candy.Auth.check()">
96
+ <a href="/profile">Profile</a>
97
+ <a href="/settings">Settings</a>
98
+ <a href="/logout">Logout</a>
99
+ <candy:else>
100
+ <a href="/login">Login</a>
101
+ <a href="/register">Register</a>
102
+ </candy:if>
103
+ </nav>
104
+ ```
105
+
106
+ #### Product Stock Status
107
+
108
+ ```html
109
+ <div class="product">
110
+ <h3><candy var="product.name" /></h3>
111
+ <p class="price">$<candy var="product.price" /></p>
112
+
113
+ <candy:if condition="product.stock > 10">
114
+ <span class="badge in-stock">In Stock</span>
115
+ <button>Add to Cart</button>
116
+ <candy:elseif condition="product.stock > 0">
117
+ <span class="badge low-stock">Only <candy var="product.stock" /> left!</span>
118
+ <button>Add to Cart</button>
119
+ <candy:else>
120
+ <span class="badge out-of-stock">Out of Stock</span>
121
+ <button disabled>Notify Me</button>
122
+ </candy:if>
123
+ </div>
124
+ ```
125
+
126
+ #### User Role-Based Content
127
+
128
+ ```html
129
+ <div class="dashboard">
130
+ <h1>Dashboard</h1>
131
+
132
+ <candy:if condition="user.role === 'admin'">
133
+ <div class="admin-section">
134
+ <h2>Admin Tools</h2>
135
+ <a href="/admin/users">Manage Users</a>
136
+ <a href="/admin/settings">System Settings</a>
137
+ <a href="/admin/logs">View Logs</a>
138
+ </div>
139
+ </candy:if>
140
+
141
+ <candy:if condition="user.role === 'admin' || user.role === 'moderator'">
142
+ <div class="moderation-section">
143
+ <h2>Moderation</h2>
144
+ <a href="/moderate/posts">Review Posts</a>
145
+ <a href="/moderate/reports">Handle Reports</a>
146
+ </div>
147
+ </candy:if>
148
+
149
+ <div class="user-section">
150
+ <h2>Your Content</h2>
151
+ <a href="/my-posts">My Posts</a>
152
+ <a href="/my-profile">My Profile</a>
153
+ </div>
154
+ </div>
155
+ ```
156
+
157
+ #### Form Validation Messages
158
+
159
+ ```html
160
+ <form>
161
+ <div class="form-group">
162
+ <label>Email</label>
163
+ <input type="email" name="email" value="<candy var="email" />">
164
+
165
+ <candy:if condition="errors && errors.email">
166
+ <span class="error"><candy var="errors.email" /></span>
167
+ </candy:if>
168
+ </div>
169
+
170
+ <div class="form-group">
171
+ <label>Password</label>
172
+ <input type="password" name="password">
173
+
174
+ <candy:if condition="errors && errors.password">
175
+ <span class="error"><candy var="errors.password" /></span>
176
+ </candy:if>
177
+ </div>
178
+
179
+ <candy:if condition="success">
180
+ <div class="success-message">
181
+ <candy var="success" />
182
+ </div>
183
+ </candy:if>
184
+
185
+ <button type="submit">Submit</button>
186
+ </form>
187
+ ```
188
+
189
+ #### Conditional CSS Classes
190
+
191
+ ```html
192
+ <div class="user-card <candy:if condition="user.isPremium">premium</candy:if> <candy:if condition="user.isVerified">verified</candy:if>">
193
+ <h3><candy var="user.name" /></h3>
194
+
195
+ <candy:if condition="user.isPremium">
196
+ <span class="badge">⭐ Premium</span>
197
+ </candy:if>
198
+
199
+ <candy:if condition="user.isVerified">
200
+ <span class="badge">✓ Verified</span>
201
+ </candy:if>
202
+ </div>
203
+ ```
204
+
205
+ #### Nested Conditions
206
+
207
+ ```html
208
+ <candy:if condition="user">
209
+ <candy:if condition="user.isActive">
210
+ <candy:if condition="user.subscription">
211
+ <candy:if condition="user.subscription.status === 'active'">
212
+ <div class="premium-content">
213
+ <h2>Premium Content</h2>
214
+ <p>Welcome, premium member!</p>
215
+ </div>
216
+ <candy:elseif condition="user.subscription.status === 'expired'">
217
+ <div class="renewal-notice">
218
+ <p>Your subscription has expired. Please renew to continue.</p>
219
+ <a href="/renew">Renew Now</a>
220
+ </div>
221
+ </candy:if>
222
+ <candy:else>
223
+ <div class="upgrade-notice">
224
+ <p>Upgrade to premium for exclusive content!</p>
225
+ <a href="/upgrade">Upgrade Now</a>
226
+ </div>
227
+ </candy:if>
228
+ <candy:else>
229
+ <div class="inactive-notice">
230
+ <p>Your account is inactive. Please contact support.</p>
231
+ </div>
232
+ </candy:if>
233
+ <candy:else>
234
+ <div class="login-notice">
235
+ <p>Please log in to view this content.</p>
236
+ <a href="/login">Login</a>
237
+ </div>
238
+ </candy:if>
239
+ ```
240
+
241
+ #### Conditional Attributes
242
+
243
+ ```html
244
+ <!-- Disabled button -->
245
+ <button <candy:if condition="!canSubmit">disabled</candy:if>>
246
+ Submit
247
+ </button>
248
+
249
+ <!-- Selected option -->
250
+ <select name="country">
251
+ <option value="tr" <candy:if condition="country === 'tr'">selected</candy:if>>Turkey</option>
252
+ <option value="us" <candy:if condition="country === 'us'">selected</candy:if>>USA</option>
253
+ <option value="uk" <candy:if condition="country === 'uk'">selected</candy:if>>UK</option>
254
+ </select>
255
+
256
+ <!-- Checked checkbox -->
257
+ <input
258
+ type="checkbox"
259
+ name="terms"
260
+ <candy:if condition="termsAccepted">checked</candy:if>
261
+ >
262
+ ```
263
+
264
+ ### Best Practices
265
+
266
+ 1. **Keep conditions simple**: Complex logic should be in the controller
267
+ 2. **Use meaningful variable names**: Make conditions self-documenting
268
+ 3. **Avoid deep nesting**: Refactor complex nested conditions
269
+ 4. **Handle null/undefined**: Always check if objects exist before accessing properties
270
+
271
+ **Good:**
272
+ ```javascript
273
+ // Controller
274
+ Candy.set('canEdit', user.isAdmin || user.id === post.authorId)
275
+ ```
276
+
277
+ ```html
278
+ <!-- View -->
279
+ <candy:if condition="canEdit">
280
+ <button>Edit</button>
281
+ </candy:if>
282
+ ```
283
+
284
+ **Avoid:**
285
+ ```html
286
+ <!-- Too complex for a view -->
287
+ <candy:if condition="(user && user.role === 'admin') || (user && post && user.id === post.authorId && !post.isLocked)">
288
+ <button>Edit</button>
289
+ </candy:if>
290
+ ```
@@ -0,0 +1,353 @@
1
+ ## 🔁 Loops and Iteration
2
+
3
+ Loops allow you to repeat content for each item in an array or object. This is essential for displaying lists, tables, and collections.
4
+
5
+ ### For Loop
6
+
7
+ The most common way to iterate over arrays and objects:
8
+
9
+ ```html
10
+ <candy:for in="users" key="index" value="user">
11
+ <div class="user-card">
12
+ <h3><candy var="user.name" /></h3>
13
+ <p><candy var="user.email" /></p>
14
+ </div>
15
+ </candy:for>
16
+ ```
17
+
18
+ **Parameters:**
19
+ - `in`: The array or object to loop through (required)
20
+ - `key`: Variable name for the index/key (optional, default: "key")
21
+ - `value`: Variable name for the value (optional, default: "value")
22
+
23
+ ### Iterating Over Arrays
24
+
25
+ ```javascript
26
+ // Controller
27
+ Candy.set('products', [
28
+ { name: 'Laptop', price: 999 },
29
+ { name: 'Mouse', price: 29 },
30
+ { name: 'Keyboard', price: 79 }
31
+ ])
32
+ ```
33
+
34
+ ```html
35
+ <!-- View -->
36
+ <div class="products">
37
+ <candy:for in="products" key="index" value="product">
38
+ <div class="product">
39
+ <span class="number"><candy var="index + 1" />.</span>
40
+ <h3><candy var="product.name" /></h3>
41
+ <p>$<candy var="product.price" /></p>
42
+ </div>
43
+ </candy:for>
44
+ </div>
45
+ ```
46
+
47
+ ### Iterating Over Objects
48
+
49
+ ```javascript
50
+ // Controller
51
+ Candy.set('settings', {
52
+ theme: 'dark',
53
+ language: 'en',
54
+ notifications: true
55
+ })
56
+ ```
57
+
58
+ ```html
59
+ <!-- View -->
60
+ <table>
61
+ <candy:for in="settings" key="settingKey" value="settingValue">
62
+ <tr>
63
+ <td><candy var="settingKey" /></td>
64
+ <td><candy var="settingValue" /></td>
65
+ </tr>
66
+ </candy:for>
67
+ </table>
68
+ ```
69
+
70
+ ### While Loop
71
+
72
+ Use while loops for conditional iteration:
73
+
74
+ ```html
75
+ <script:candy>
76
+ let counter = 0;
77
+ </script:candy>
78
+
79
+ <candy:while condition="counter < 5">
80
+ <p>Item <candy var="counter + 1" /></p>
81
+ <script:candy>counter++;</script:candy>
82
+ </candy:while>
83
+ ```
84
+
85
+ **Note:** Be careful with while loops to avoid infinite loops. The condition must eventually become false.
86
+
87
+ ### Loop Control Statements
88
+
89
+ #### Break
90
+
91
+ Exit the loop early:
92
+
93
+ ```html
94
+ <candy:for in="products" value="product">
95
+ <candy:if condition="product.stock === 0">
96
+ <p class="notice">Some products are out of stock</p>
97
+ <candy:break />
98
+ </candy:if>
99
+ <div><candy var="product.name" /></div>
100
+ </candy:for>
101
+ ```
102
+
103
+ #### Continue
104
+
105
+ Skip to the next iteration:
106
+
107
+ ```html
108
+ <candy:for in="users" value="user">
109
+ <candy:if condition="user.isBlocked">
110
+ <candy:continue />
111
+ </candy:if>
112
+
113
+ <div class="user">
114
+ <h3><candy var="user.name" /></h3>
115
+ <p><candy var="user.email" /></p>
116
+ </div>
117
+ </candy:for>
118
+ ```
119
+
120
+ ### Practical Examples
121
+
122
+ #### Product List with Numbering
123
+
124
+ ```html
125
+ <div class="product-list">
126
+ <h2>Our Products</h2>
127
+
128
+ <candy:for in="products" key="i" value="product">
129
+ <div class="product-item">
130
+ <span class="number">#<candy var="i + 1" /></span>
131
+ <img src="<candy var="product.image" />" alt="<candy var="product.name" />">
132
+ <h3><candy var="product.name" /></h3>
133
+ <p class="price">$<candy var="product.price" /></p>
134
+
135
+ <candy:if condition="product.discount">
136
+ <span class="discount">-<candy var="product.discount" />%</span>
137
+ </candy:if>
138
+ </div>
139
+ </candy:for>
140
+ </div>
141
+ ```
142
+
143
+ #### Table with Data
144
+
145
+ ```html
146
+ <table class="users-table">
147
+ <thead>
148
+ <tr>
149
+ <th>#</th>
150
+ <th>Name</th>
151
+ <th>Email</th>
152
+ <th>Role</th>
153
+ <th>Status</th>
154
+ </tr>
155
+ </thead>
156
+ <tbody>
157
+ <candy:for in="users" key="index" value="user">
158
+ <tr>
159
+ <td><candy var="index + 1" /></td>
160
+ <td><candy var="user.name" /></td>
161
+ <td><candy var="user.email" /></td>
162
+ <td><candy var="user.role" /></td>
163
+ <td>
164
+ <candy:if condition="user.isActive">
165
+ <span class="badge success">Active</span>
166
+ <candy:else>
167
+ <span class="badge danger">Inactive</span>
168
+ </candy:if>
169
+ </td>
170
+ </tr>
171
+ </candy:for>
172
+ </tbody>
173
+ </table>
174
+ ```
175
+
176
+ #### Nested Loops
177
+
178
+ ```html
179
+ <div class="categories">
180
+ <candy:for in="categories" value="category">
181
+ <div class="category">
182
+ <h2><candy var="category.name" /></h2>
183
+
184
+ <div class="products">
185
+ <candy:for in="category.products" value="product">
186
+ <div class="product">
187
+ <h3><candy var="product.name" /></h3>
188
+ <p>$<candy var="product.price" /></p>
189
+ </div>
190
+ </candy:for>
191
+ </div>
192
+ </div>
193
+ </candy:for>
194
+ </div>
195
+ ```
196
+
197
+ #### Grid Layout
198
+
199
+ ```html
200
+ <div class="grid">
201
+ <candy:for in="items" key="i" value="item">
202
+ <div class="grid-item">
203
+ <img src="<candy var="item.image" />" alt="<candy var="item.title" />">
204
+ <h3><candy var="item.title" /></h3>
205
+ <p><candy var="item.description" /></p>
206
+
207
+ <!-- Add row break every 3 items -->
208
+ <candy:if condition="(i + 1) % 3 === 0">
209
+ <div class="row-break"></div>
210
+ </candy:if>
211
+ </div>
212
+ </candy:for>
213
+ </div>
214
+ ```
215
+
216
+ #### Filtering with Continue
217
+
218
+ ```html
219
+ <div class="active-users">
220
+ <h2>Active Users</h2>
221
+
222
+ <candy:for in="users" value="user">
223
+ <!-- Skip inactive users -->
224
+ <candy:if condition="!user.isActive">
225
+ <candy:continue />
226
+ </candy:if>
227
+
228
+ <!-- Skip blocked users -->
229
+ <candy:if condition="user.isBlocked">
230
+ <candy:continue />
231
+ </candy:if>
232
+
233
+ <div class="user-card">
234
+ <h3><candy var="user.name" /></h3>
235
+ <p><candy var="user.email" /></p>
236
+ </div>
237
+ </candy:for>
238
+ </div>
239
+ ```
240
+
241
+ #### Empty State Handling
242
+
243
+ ```html
244
+ <div class="products-section">
245
+ <h2>Products</h2>
246
+
247
+ <candy:if condition="products && products.length > 0">
248
+ <div class="products-grid">
249
+ <candy:for in="products" value="product">
250
+ <div class="product-card">
251
+ <h3><candy var="product.name" /></h3>
252
+ <p>$<candy var="product.price" /></p>
253
+ </div>
254
+ </candy:for>
255
+ </div>
256
+ <candy:else>
257
+ <div class="empty-state">
258
+ <p>No products found.</p>
259
+ <a href="/products/add">Add your first product</a>
260
+ </div>
261
+ </candy:if>
262
+ </div>
263
+ ```
264
+
265
+ #### Alternating Row Colors
266
+
267
+ ```html
268
+ <table>
269
+ <candy:for in="items" key="i" value="item">
270
+ <tr class="<candy:if condition="i % 2 === 0">even<candy:else>odd</candy:if>">
271
+ <td><candy var="item.name" /></td>
272
+ <td><candy var="item.value" /></td>
273
+ </tr>
274
+ </candy:for>
275
+ </table>
276
+ ```
277
+
278
+ #### Limited Results with Break
279
+
280
+ ```html
281
+ <div class="top-products">
282
+ <h2>Top 5 Products</h2>
283
+
284
+ <script:candy>
285
+ let count = 0;
286
+ </script:candy>
287
+
288
+ <candy:for in="products" value="product">
289
+ <candy:if condition="count >= 5">
290
+ <candy:break />
291
+ </candy:if>
292
+
293
+ <div class="product">
294
+ <h3><candy var="product.name" /></h3>
295
+ <p>$<candy var="product.price" /></p>
296
+ </div>
297
+
298
+ <script:candy>count++;</script:candy>
299
+ </candy:for>
300
+ </div>
301
+ ```
302
+
303
+ #### Pagination with While
304
+
305
+ ```html
306
+ <script:candy>
307
+ const itemsPerPage = 10;
308
+ const currentPage = parseInt(Candy.Request.get('page')) || 1;
309
+ const startIndex = (currentPage - 1) * itemsPerPage;
310
+ const endIndex = startIndex + itemsPerPage;
311
+ let index = startIndex;
312
+ </script:candy>
313
+
314
+ <div class="items">
315
+ <candy:while condition="index < endIndex && index < items.length">
316
+ <div class="item">
317
+ <candy var="items[index].name" />
318
+ </div>
319
+ <script:candy>index++;</script:candy>
320
+ </candy:while>
321
+ </div>
322
+ ```
323
+
324
+ ### Best Practices
325
+
326
+ 1. **Check for existence**: Always verify the array/object exists before looping
327
+ 2. **Use meaningful names**: Choose descriptive variable names for keys and values
328
+ 3. **Avoid complex logic**: Keep loop bodies simple, move complex logic to controllers
329
+ 4. **Handle empty states**: Always provide feedback when there are no items
330
+ 5. **Be careful with while**: Ensure while loops will eventually terminate
331
+
332
+ **Good:**
333
+ ```javascript
334
+ // Controller - prepare data
335
+ Candy.set('activeUsers', users.filter(u => u.isActive))
336
+ ```
337
+
338
+ ```html
339
+ <!-- View - simple loop -->
340
+ <candy:for in="activeUsers" value="user">
341
+ <div><candy var="user.name" /></div>
342
+ </candy:for>
343
+ ```
344
+
345
+ **Avoid:**
346
+ ```html
347
+ <!-- Too complex for a view -->
348
+ <candy:for in="users" value="user">
349
+ <candy:if condition="user.isActive && !user.isBlocked && user.role !== 'guest'">
350
+ <div><candy var="user.name" /></div>
351
+ </candy:if>
352
+ </candy:for>
353
+ ```