0 Votes

Changes for page Todolist Macro

Last modified by Ryan C on 2025/04/30 07:54

From version 27.1
edited by Ryan C
on 2025/04/24 11:25
Change comment: There is no comment for this version
To version 9.1
edited by Ryan C
on 2025/04/24 10:29
Change comment: There is no comment for this version

Summary

Details

Page properties
Content
... ... @@ -1,9 +1,7 @@
1 1  Macro to display todo-lists using emberjs. This code is based on the emberjs demo. You can see an example below
2 2  
3 3  {{todolist width="50%" center="1"}}
4 -a 3nd thing|0
5 -adding this thing|0
6 -adding this thing|0
7 -refreshed they are still there not even having to save page|0
4 +a thing|1
5 +a second thing|0
8 8  
9 9  {{/todolist}}
XWiki.JavaScriptExtension[0]
Code
... ... @@ -1,348 +1,257 @@
1 1  requirejs.config({
2 - baseUrl: '${xwiki.getDocument("TodoLists.TodoListMacro").getURL("download")}/',
3 - paths: {
4 - // Use existing jQuery if available, otherwise load 1.10.2
5 - 'jquery': window.jQuery ? 'empty:' : 'https://code.jquery.com/jquery-1.10.2.min'
6 - },
7 - map: {
8 - // Map any module requiring 'jquery' to use the global jQuery
9 - '*': { 'jquery': window.jQuery ? 'jquery-global' : 'jquery' }
10 - },
11 - shim: {
12 - 'ember-data': {
13 - deps: ['ember', 'jquery']
2 + //By default load any module IDs from js/lib
3 + baseUrl: '${xwiki.getDocument("TodoLists.TodoListMacro").getURL("download")}/',
4 + //except, if the module ID starts with "app",
5 + //load it from the js/app directory. paths
6 + //config is relative to the baseUrl, and
7 + //never includes a ".js" extension since
8 + //the paths config could be for a directory.
9 + paths: {
14 14   },
15 - 'ember': {
16 - deps: ['jquery']
11 + shim: {
12 + 'ember-data': {
13 + deps: ['ember', 'jquery']
14 + },
15 + 'ember': {
16 + deps: ['jquery']
17 + }
17 17   }
18 - }
19 19  });
20 +require(['jquery', 'handlebars-v1.2.1', 'ember-data', 'ember' ], function ($) {
20 20  
21 -// Define a module that returns the global jQuery
22 -define('jquery-global', [], function() {
23 - return window.jQuery;
22 +// create app
23 +window.Todos = Ember.Application.create({
24 + rootElement: '#todoappdiv'
24 24  });
25 25  
26 -// Define empty module for when jQuery is already loaded
27 -define('empty:', [], function() {
28 - return window.jQuery;
27 +// define the todolist store that will write to the TodoListsService
28 +Todos.ApplicationAdapter = DS.Adapter.extend({
29 + createRecord: function(store, type, record) {
30 + console.log("createRecord");
31 + var query = { create: "1", content : JSON.stringify(record) };
32 + return new Ember.RSVP.Promise(function(resolve, reject) {
33 + jQuery.getJSON("${xwiki.getURL("TodoLists.TodoListsService")}?page=" + XWiki.currentSpace + "." + XWiki.currentPage + "&xpage=plain&outputSyntax=plain", query).then(function(data) {
34 + Ember.run(null, resolve, data);
35 + }, function(jqXHR) {
36 + jqXHR.then = null; // tame jQuery's ill mannered promises
37 + Ember.run(null, reject, jqXHR);
38 + });
39 + });
40 + },
41 + deleteRecord: function(store, type, record) {
42 + console.log("deleteRecord");
43 + return this.saveAll(store.recordArrayManager.recordArraysForRecord(record).list[0].content);
44 + },
45 + find: function(store, type, id) {
46 + console.log("find");
47 + return;
48 + },
49 + findAll: function(store, type, sinceToken) {
50 + console.log("findAll");
51 + var query = { since: sinceToken };
52 + return new Ember.RSVP.Promise(function(resolve, reject) {
53 + jQuery.getJSON("${xwiki.getURL("TodoLists.TodoListsService")}?page=" + XWiki.currentSpace + "." + XWiki.currentPage + "&xpage=plain&outputSyntax=plain", query).then(function(data) {
54 + Ember.run(null, resolve, data);
55 + }, function(jqXHR) {
56 + jqXHR.then = null; // tame jQuery's ill mannered promises
57 + Ember.run(null, reject, jqXHR);
58 + });
59 + });
60 + },
61 + updateRecord: function(store, type, record) {
62 + return this.saveAll(store.recordArrayManager.recordArraysForRecord(record).list[0].content);
63 + },
64 + saveAll: function(alldata) {
65 + console.log("updateRecord");
66 + var query = { save: "1", content : JSON.stringify(alldata) };
67 + return new Ember.RSVP.Promise(function(resolve, reject) {
68 + jQuery.getJSON("${xwiki.getURL("TodoLists.TodoListsService")}?page=" + XWiki.currentSpace + "." + XWiki.currentPage + "&xpage=plain&outputSyntax=plain", query).then(function(data) {
69 + Ember.run(null, resolve, data);
70 + }, function(jqXHR) {
71 + jqXHR.then = null; // tame jQuery's ill mannered promises
72 + Ember.run(null, reject, jqXHR);
73 + });
74 + });
75 + }
29 29  });
30 30  
31 -require(['jquery', 'handlebars-v1.2.1', 'ember-data', 'ember'], function($) {
32 - // Check jQuery version compatibility with Ember
33 - var jQueryVersion = $.fn.jquery.split('.');
34 - var majorVersion = parseInt(jQueryVersion[0]);
35 - var minorVersion = parseInt(jQueryVersion[1]);
78 +// use my own store
79 +Todos.store = DS.Store.create({
80 + adapter: 'ApplicationAdapter'
81 +});
36 36  
37 - console.log("Using jQuery version:", $.fn.jquery);
83 +// Todos.ApplicationAdapter = DS.FixtureAdapter.extend();
38 38  
39 - // Verify jQuery version is compatible with Ember
40 - var isCompatible = (majorVersion === 1 && minorVersion >= 7 && minorVersion <= 10) ||
41 - (majorVersion === 2 && minorVersion === 0);
85 +// routing
86 +Todos.Router.map(function() {
87 + this.resource('todos', { path: '' }, function() {
88 + this.route('active');
89 + this.route('completed');
90 + });
91 +});
42 42  
43 - if (!isCompatible) {
44 - console.error("Warning: Ember Views require jQuery 1.7, 1.8, 1.9, 1.10, or 2.0. Current version:", $.fn.jquery);
45 - // Try to load a compatible version if needed
46 - require(['https://code.jquery.com/jquery-1.10.2.min.js'], function(compatibleJQuery) {
47 - // Store the original jQuery
48 - var originalJQuery = window.jQuery;
49 - // Replace with compatible jQuery temporarily
50 - window.jQuery = compatibleJQuery;
51 - // Continue with the rest of the code
52 - initializeEmberApp(compatibleJQuery);
53 - // Restore original jQuery when done
54 - window.jQuery = originalJQuery;
55 - });
56 - } else {
57 - // jQuery version is compatible, proceed normally
58 - initializeEmberApp($);
93 +Todos.TodosRoute = Ember.Route.extend({
94 + model: function() {
95 + return this.store.find('todo');
59 59   }
97 +});
60 60  
61 - function initializeEmberApp($) {
62 - // Create app
63 - window.Todos = Ember.Application.create({
64 - rootElement: '#todoappdiv'
65 - });
99 +Todos.TodosIndexRoute = Ember.Route.extend({
100 + model: function() {
101 + return this.modelFor('todos');
102 + }
103 +});
66 66  
67 - // Define the todolist store that will write to the TodoListsService
68 - Todos.ApplicationAdapter = DS.Adapter.extend({
69 - namespace: 'api', // Moved inside the adapter definition
70 -
71 - createRecord: function(store, type, record) {
72 - console.log("createRecord");
73 - var data = this.serialize(record, { includeId: true });
74 - var query = { create: "1", content: JSON.stringify(data) };
75 -
76 - return new Ember.RSVP.Promise(function(resolve, reject) {
77 - var url = "${xwiki.getURL('TodoLists.TodoListsService')}?page=" +
78 - XWiki.currentSpace + "." + XWiki.currentPage +
79 - "&xpage=plain&outputSyntax=plain";
80 - console.log("Creating record, URL:", url);
81 -
82 - jQuery.getJSON(url, query).then(function(data) {
83 - console.log("Create success:", data);
84 - Ember.run(function() {
85 - resolve(data);
86 - });
87 - }, function(jqXHR) {
88 - console.error("Create error:", jqXHR);
89 - Ember.run(function() {
90 - reject(jqXHR);
91 - });
92 - });
93 - });
94 - },
95 -
96 - deleteRecord: function(store, type, record) {
97 - console.log("deleteRecord");
98 - var recordArrays = store.recordArrayManager.recordArraysForRecord(record);
99 - if (recordArrays && recordArrays.list && recordArrays.list[0]) {
100 - return this.saveAll(recordArrays.list[0].content);
101 - }
102 - return Ember.RSVP.resolve();
103 - },
104 -
105 - find: function(store, type, id) {
106 - console.log("find");
107 - return Ember.RSVP.resolve();
108 - },
109 -
110 - findAll: function(store, type, sinceToken) {
111 - console.log("findAll");
112 - var query = { since: sinceToken };
113 -
114 - return new Ember.RSVP.Promise(function(resolve, reject) {
115 - var url = "${xwiki.getURL('TodoLists.TodoListsService')}?page=" +
116 - XWiki.currentSpace + "." + XWiki.currentPage +
117 - "&xpage=plain&outputSyntax=plain";
118 - console.log("Finding all records, URL:", url);
119 -
120 - jQuery.getJSON(url, query).then(function(data) {
121 - console.log("FindAll success:", data);
122 - Ember.run(function() {
123 - resolve(data);
124 - });
125 - }, function(jqXHR) {
126 - console.error("FindAll error:", jqXHR);
127 - Ember.run(function() {
128 - reject(jqXHR);
129 - });
130 - });
131 - });
132 - },
133 -
134 - updateRecord: function(store, type, record) {
135 - console.log("updateRecord");
136 - var recordArrays = store.recordArrayManager.recordArraysForRecord(record);
137 - if (recordArrays && recordArrays.list && recordArrays.list[0]) {
138 - return this.saveAll(recordArrays.list[0].content);
139 - }
140 - return Ember.RSVP.resolve();
141 - },
142 -
143 - saveAll: function(alldata) {
144 - console.log("saveAll");
145 - var query = { save: "1", content: JSON.stringify(alldata) };
146 -
147 - return new Ember.RSVP.Promise(function(resolve, reject) {
148 - var currentPage = XWiki.currentSpace + "." + XWiki.currentPage;
149 - var url = "${xwiki.getURL('TodoLists.TodoListsService')}?page=" +
150 - currentPage + "&xpage=plain&outputSyntax=plain";
151 - console.log("Saving all records, URL:", url, "Page:", currentPage);
152 -
153 - jQuery.getJSON(url, query).then(function(data) {
154 - console.log("SaveAll success:", data);
155 - Ember.run(function() {
156 - resolve(data);
157 - });
158 - }, function(jqXHR) {
159 - console.error("SaveAll error:", jqXHR);
160 - Ember.run(function() {
161 - reject(jqXHR);
162 - });
163 - });
164 - });
165 - }
105 +// ... additional lines truncated for brevity ...
106 +Todos.TodosActiveRoute = Ember.Route.extend({
107 + model: function(){
108 + return this.store.filter('todo', function(todo) {
109 + return !todo.get('isCompleted');
166 166   });
111 + },
112 + renderTemplate: function(controller) {
113 + this.render('todos/index', {controller: controller});
114 + }
115 +});
167 167  
168 - // Routing
169 - Todos.Router.map(function() {
170 - this.resource('todos', { path: '' }, function() {
171 - this.route('active');
172 - this.route('completed');
173 - });
117 +Todos.TodosCompletedRoute = Ember.Route.extend({
118 + model: function(){
119 + return this.store.filter('todo', function (todo) {
120 + return todo.get('isCompleted');
174 174   });
122 + },
123 + renderTemplate: function(controller){
124 + this.render('todos/index', {controller: controller});
125 + }
126 +});
175 175  
176 - Todos.TodosRoute = Ember.Route.extend({
177 - model: function() {
178 - return this.store.find('todo');
179 - }
180 - });
128 +// data model
129 +Todos.Todo = DS.Model.extend({
130 + title: DS.attr('string'),
131 + priority: DS.attr('string'),
132 + assignee: DS.attr('string'),
133 + isCompleted: DS.attr('boolean')
134 +});
181 181  
182 - Todos.TodosIndexRoute = Ember.Route.extend({
183 - model: function() {
184 - return this.modelFor('todos');
185 - }
186 - });
136 +// ... additional lines truncated for brevity ...
137 +Todos.Todo.FIXTURES = [
138 + {
139 + id: 1,
140 + title: 'Learn Ember.js X',
141 + isCompleted: true
142 + },
143 + {
144 + id: 2,
145 + title: '...',
146 + isCompleted: false
147 + },
148 + {
149 + id: 3,
150 + title: 'Profit!',
151 + isCompleted: false
152 + }
153 +];
187 187  
188 - Todos.TodosActiveRoute = Ember.Route.extend({
189 - model: function() {
190 - return this.store.filter('todo', function(todo) {
191 - return !todo.get('isCompleted');
192 - });
193 - },
194 - renderTemplate: function(controller) {
195 - this.render('todos/index', { controller: controller });
196 - }
197 - });
155 +// controler
156 +Todos.TodoController = Ember.ObjectController.extend({
157 + actions: {
158 + editTodo: function () {
159 + this.set('isEditing', true);
160 + },
161 + acceptChanges: function() {
162 + this.set('isEditing', false);
198 198  
199 - Todos.TodosCompletedRoute = Ember.Route.extend({
200 - model: function() {
201 - return this.store.filter('todo', function(todo) {
202 - return todo.get('isCompleted');
203 - });
204 - },
205 - renderTemplate: function(controller) {
206 - this.render('todos/index', { controller: controller });
207 - }
208 - });
164 + if (Ember.isEmpty(this.get('model.title'))) {
165 + this.send('removeTodo');
166 + } else {
167 + this.get('model').save();
168 + }
169 + },
170 + removeTodo: function() {
171 + var todo = this.get('model');
172 + todo.deleteRecord();
173 + todo.save();
174 + }
175 + },
209 209  
210 - // Data model
211 - Todos.Todo = DS.Model.extend({
212 - title: DS.attr('string'),
213 - priority: DS.attr('string'),
214 - assignee: DS.attr('string'),
215 - isCompleted: DS.attr('boolean')
216 - });
177 + isEditing: false,
217 217  
218 - // Controller
219 - Todos.TodoController = Ember.ObjectController.extend({
220 - actions: {
221 - editTodo: function() {
222 - this.set('isEditing', true);
223 - },
224 - acceptChanges: function() {
225 - this.set('isEditing', false);
179 + isCompleted: function(key, value){
180 + var model = this.get('model');
226 226  
227 - if (Ember.isEmpty(this.get('model.title'))) {
228 - this.send('removeTodo');
229 - } else {
230 - this.get('model').save();
231 - }
232 - },
233 - removeTodo: function() {
234 - var todo = this.get('model');
235 - todo.deleteRecord();
236 - todo.save();
237 - }
238 - },
182 + if (value === undefined) {
183 + // property being used as a getter
184 + return model.get('isCompleted');
185 + } else {
186 + // property being used as setter
187 + model.set('isCompleted', value);
188 + model.save();
189 + return value;
190 + }
191 + }.property('model.isCompleted')
192 +});
239 239  
240 - isEditing: false,
194 +Todos.TodosController = Ember.ArrayController.extend({
195 + actions: {
196 + createTodo: function () {
197 + // Get the todo title set by the "New Todo" text field
198 + var title = this.get('newTitle');
199 + if (!title.trim()) { return; }
241 241  
242 - isCompleted: function(key, value) {
243 - var model = this.get('model');
201 + // Create the new Todo model
202 + var todo = this.store.createRecord('todo', {
203 + title: title,
204 + priority: "P3",
205 + assignee: "all",
206 + isCompleted: false
207 + });
244 244  
245 - if (value === undefined) {
246 - // Property being used as a getter
247 - return model.get('isCompleted');
248 - } else {
249 - // Property being used as setter
250 - model.set('isCompleted', value);
251 - model.save();
252 - return value;
253 - }
254 - }.property('model.isCompleted')
255 - });
209 + // Clear the "New Todo" text field
210 + this.set('newTitle', '');
256 256  
257 - Todos.TodosController = Ember.ArrayController.extend({
258 - actions: {
259 - createTodo: function() {
260 - // Get the todo title set by the "New Todo" text field
261 - var title = this.get('newTitle');
262 - if (!title || !title.trim()) { return; }
212 + // Save the new model
213 + todo.save();
214 + },
215 + clearCompleted: function () {
216 + var completed = this.filterProperty('isCompleted', true);
217 + completed.invoke('deleteRecord');
218 + completed.invoke('save');
219 + }
220 + },
263 263  
264 - console.log("Creating new todo with title:", title);
222 + remaining: function () {
223 + return this.filterProperty('isCompleted', false).get('length');
224 + }.property('@each.isCompleted'),
265 265  
266 - // Create the new Todo model
267 - var todo = this.store.createRecord('todo', {
268 - title: title,
269 - priority: "P3",
270 - assignee: "all",
271 - isCompleted: false
272 - });
226 + inflection: function () {
227 + var remaining = this.get('remaining');
228 + return remaining === 1 ? 'item' : 'items';
229 + }.property('remaining'),
230 + hasCompleted: function () {
231 + return this.get('completed') > 0;
232 + }.property('completed'),
273 273  
274 - // Clear the "New Todo" text field
275 - this.set('newTitle', '');
234 + completed: function () {
235 + return this.filterProperty('isCompleted', true).get('length');
236 + }.property('@each.isCompleted'),
276 276  
277 - // Save the new model
278 - todo.save().then(function(savedTodo) {
279 - console.log("Todo saved successfully:", savedTodo);
280 - // Force a refresh of the list after saving
281 - Ember.run.later(function() {
282 - console.log("Refreshing todo list");
283 - Todos.store.find('todo');
284 - }, 100);
285 - }, function(error) {
286 - console.error("Failed to save todo:", error);
287 - });
288 - },
238 + allAreDone: function(key, value) {
239 + if (value === undefined) {
240 + return !!this.get('length') && this.everyProperty('isCompleted', true);
241 + } else {
242 + this.setEach('isCompleted', value);
243 + this.invoke('save');
244 + return value;
245 + }
246 +}.property('@each.isCompleted')
247 +});
289 289  
290 - clearCompleted: function() {
291 - var completed = this.filter(function(todo) {
292 - return todo.get('isCompleted');
293 - });
294 -
295 - completed.forEach(function(todo) {
296 - todo.deleteRecord();
297 - todo.save();
298 - });
299 - }
300 - },
301 -
302 - remaining: function() {
303 - return this.filter(function(todo) {
304 - return !todo.get('isCompleted');
305 - }).get('length');
306 - }.property('@each.isCompleted'),
307 -
308 - inflection: function() {
309 - var remaining = this.get('remaining');
310 - return remaining === 1 ? 'item' : 'items';
311 - }.property('remaining'),
312 -
313 - hasCompleted: function() {
314 - return this.get('completed') > 0;
315 - }.property('completed'),
316 -
317 - completed: function() {
318 - return this.filter(function(todo) {
319 - return todo.get('isCompleted');
320 - }).get('length');
321 - }.property('@each.isCompleted'),
322 -
323 - allAreDone: function(key, value) {
324 - if (value === undefined) {
325 - return this.get('length') > 0 && this.every(function(todo) {
326 - return todo.get('isCompleted');
327 - });
328 - } else {
329 - this.forEach(function(todo) {
330 - todo.set('isCompleted', value);
331 - todo.save();
332 - });
333 - return value;
334 - }
335 - }.property('@each.isCompleted')
336 - });
337 -
338 - // Editing
339 - Todos.EditTodoView = Ember.TextField.extend({
340 - didInsertElement: function() {
341 - this.$().focus();
342 - }
343 - });
344 -
345 - Ember.Handlebars.helper('edit-todo', Todos.EditTodoView);
249 +// editing
250 +Todos.EditTodoView = Ember.TextField.extend({
251 + didInsertElement: function() {
252 + this.$().focus();
346 346   }
347 347  });
348 348  
256 +Ember.Handlebars.helper('edit-todo', Todos.EditTodoView);
257 +});
XWiki.WikiMacroClass[0]
Cached
... ... @@ -1,1 +1,0 @@
1 -No
Context elements
... ... @@ -1,1 +1,0 @@
1 -User
Asynchronous rendering
... ... @@ -1,1 +1,0 @@
1 -No