Revision 8fdaae3a
Von Kivitendo Admin vor mehr als 1 Jahr hinzugefügt
js/knockout.kivitendo.js | ||
---|---|---|
1 |
// bindingHandlers |
|
2 |
|
|
3 |
ko.bindingHandlers.toggle = { |
|
4 |
init: function(element, valueAccessor) { |
|
5 |
var value = valueAccessor(); |
|
6 |
ko.applyBindingsToNode(element, { |
|
7 |
click: function() { |
|
8 |
value(!value()); |
|
9 |
} |
|
10 |
}); |
|
11 |
} |
|
12 |
}; |
|
13 |
|
|
14 |
ko.bindingHandlers.formatted_date = { |
|
15 |
update: function(element, valueAccessor, allBindingsAccessor) { |
|
16 |
// console.log('valueAccessor = ' + valueAccessor()); |
|
17 |
var value = ko.utils.unwrapObservable(valueAccessor()) || undefined; |
|
18 |
// console.log("formatted_date value = " + value); |
|
19 |
if ( value !== undefined ) { |
|
20 |
$(element).text( kivi.format_date(value)); |
|
21 |
} |
|
22 |
} |
|
23 |
}; |
|
24 |
|
|
25 |
ko.bindingHandlers.parsed_formatted_date = { |
|
26 |
update: function(element, valueAccessor, allBindingsAccessor) { |
|
27 |
var value = ko.utils.unwrapObservable(valueAccessor()) || undefined; |
|
28 |
$(element).text( kivi.parse_date(kivi.format_date(value))); |
|
29 |
} |
|
30 |
}; |
|
31 |
|
|
32 |
ko.bindingHandlers.datepicker = { |
|
33 |
init: function(element, valueAccessor, allBindingsAccessor) { |
|
34 |
var $el = $(element); |
|
35 |
|
|
36 |
//initialize datepicker with some optional options |
|
37 |
var options = allBindingsAccessor().datepickerOptions || { |
|
38 |
showOn: "button", |
|
39 |
showButtonPanel: true, |
|
40 |
changeMonth: true, |
|
41 |
changeYear: true, |
|
42 |
buttonImage: "image/calendar.png", |
|
43 |
buttonImageOnly: true |
|
44 |
}; |
|
45 |
// $.datepicker.setDefaults( $.datepicker.regional[ kivi.myconfg.countrycode ] ); |
|
46 |
$el.datepicker(options); |
|
47 |
|
|
48 |
//handle the field changing |
|
49 |
// debugger; |
|
50 |
ko.utils.registerEventHandler(element, "change", function() { |
|
51 |
// update observable whenever input value changes |
|
52 |
|
|
53 |
// the change event is fired after datepicker internally sets the date and then updates the input |
|
54 |
// at this point viewModel.mydata (valueAccessor) still contains the old value |
|
55 |
// and we can update/sync it with the value from getpicker.getDate |
|
56 |
|
|
57 |
// but date may also have been changed manually by user, e.g. using the shortcut 2604 |
|
58 |
|
|
59 |
// console.log('element contains ' + $(element).val()); |
|
60 |
// console.log('event: change event on element, setting observable via getDate: ' + $el.datepicker("getDate")); |
|
61 |
var observable = valueAccessor(); // should contain the current date object in viewModel.myData |
|
62 |
|
|
63 |
var input_date; |
|
64 |
if ( $(element).val() === "" ) { |
|
65 |
// don't try parsing if element is empty |
|
66 |
input_date = null; |
|
67 |
} else { |
|
68 |
console.log('about to parse'); |
|
69 |
input_date = kivi.parse_date($(element).val()); |
|
70 |
console.log( "isNaN = " + isNaN(input_date.getTime) ); |
|
71 |
} |
|
72 |
|
|
73 |
var picker_date = $el.datepicker("getDate"); |
|
74 |
// console.log("input_date = " + kivi.format_date(input_date) + ' picker_date = ' + kivi.format_date(picker_date)); |
|
75 |
// console.log("input_date - picker_date = " + (input_date - picker_date)); |
|
76 |
|
|
77 |
// if input was updated via datepicker, getDate will already contain the desired date, use that (observable(picker_date)) |
|
78 |
// and input_date and picker_date will match! |
|
79 |
// but if input was updated manually, use the parsed_date and also update picker |
|
80 |
|
|
81 |
if ( input_date !== null && picker_date !== null && input_date - picker_date === 0 ) { |
|
82 |
console.log('input_date and picker_date are identical (set by picker), set observable to picker_date'); |
|
83 |
observable(picker_date); |
|
84 |
} else if ( input_date === null ) { |
|
85 |
observable(""); // TODO: check that it is a date object, have we validated? |
|
86 |
// console.log('input_date and picker_date differ (input field set manually), set observable to input_date'); |
|
87 |
} else { |
|
88 |
observable(input_date); // TODO: check that it is a date object, have we validated? |
|
89 |
} |
|
90 |
|
|
91 |
// observable(new_date); |
|
92 |
// return; |
|
93 |
|
|
94 |
// console.log('event: unwrapped observable: ' + ko.unwrap(observable)); |
|
95 |
// console.log('event: observable: ' + observable()); |
|
96 |
// observable still contains the old date Object from vm |
|
97 |
// debugger; |
|
98 |
// setTimeout(function(){ |
|
99 |
// console.log("running after timeout"); |
|
100 |
// observable($el.datepicker("getDate")); |
|
101 |
// }, 10); |
|
102 |
}); |
|
103 |
|
|
104 |
//handle disposal (if KO removes by the template binding) |
|
105 |
ko.utils.domNodeDisposal.addDisposeCallback(element, function() { |
|
106 |
$el.datepicker("destroy"); |
|
107 |
}); |
|
108 |
|
|
109 |
}, |
|
110 |
update: function(element, valueAccessor) { |
|
111 |
console.log("the value of the observable was changed, so now we need to update the datepicker"); |
|
112 |
var value = ko.utils.unwrapObservable(valueAccessor()), |
|
113 |
$el = $(element), |
|
114 |
current = $el.datepicker("getDate"); |
|
115 |
var observable = valueAccessor(); // should contain a date object |
|
116 |
// debugger; |
|
117 |
console.log("observabl: " + ko.unwrap(observable)); |
|
118 |
console.log("update: value = " + value); |
|
119 |
console.log("update: current = " + current); |
|
120 |
console.log("update: el.val = " + $el.val()); |
|
121 |
console.log("update: el.val = " + $el.val()); |
|
122 |
|
|
123 |
// if ( isNaN( ko.unwrap(observable())).getTime() ) { |
|
124 |
// console.log("isNaN"); |
|
125 |
// } else { |
|
126 |
// $el.datepicker("setDate", value); |
|
127 |
// } |
|
128 |
|
|
129 |
// if ( current !== null && $el.val() !== '' ) { //$el.val() !== null && curr) { |
|
130 |
// var parsed_date = kivi.parse_date($el.val()); |
|
131 |
// var formatted_date = kivi.format_date(parsed_date); |
|
132 |
// console.log("parsed_date = " + parsed_date + ", formatted_date = " + formatted_date); |
|
133 |
// $el.datepicker("setDate", parsed_date); |
|
134 |
// valueAccessor(parsed_date); |
|
135 |
// } |
|
136 |
if (value - current !== 0) { |
|
137 |
console.log('setting date via setDate to ' + value); |
|
138 |
$el.datepicker("setDate", value); |
|
139 |
} |
|
140 |
} |
|
141 |
}; |
|
142 |
|
|
143 |
ko.bindingHandlers.formatted_amount = { |
|
144 |
update: function(element, valueAccessor, allBindingsAccessor) { |
|
145 |
var value = ko.utils.unwrapObservable(valueAccessor()) || undefined; |
|
146 |
var precision = ko.utils.unwrapObservable(allBindingsAccessor().precision) || 2; |
|
147 |
var type = ko.utils.unwrapObservable(allBindingsAccessor().type) || ''; |
|
148 |
|
|
149 |
// automatically add class numeric so we don't have to do it in template |
|
150 |
// useful for td |
|
151 |
// debugger; |
|
152 |
if ( element.tagName === 'TD' ) { // other tagnames: SPAN |
|
153 |
// maybe have it as an option in allBindingsAccessor?, e.g. |
|
154 |
// var numeric = ko.utils.unwrapObservable(allBindingsAccessor().numeric) || ''; |
|
155 |
// if ( numeric ) { |
|
156 |
$(element).addClass('numeric'); |
|
157 |
// } |
|
158 |
} |
|
159 |
// console.log('updating formatted_amount bH with reference ' + $(element).parent().find('td:eq(1)').html() + ' and value ' + value); |
|
160 |
if ( value !== undefined ) { |
|
161 |
value = + value; |
|
162 |
if ( type === 'cd' ) { |
|
163 |
$(element).text( format_sh(value) ); |
|
164 |
} else { |
|
165 |
// console.log('cd'); |
|
166 |
var formattedAmount = kivi.format_amount(value, precision); |
|
167 |
$(element).text(formattedAmount); |
|
168 |
} |
|
169 |
} else { |
|
170 |
$(element).text(''); // don't return undefined here, otherwise deselecting the last element won't work (updating sums) |
|
171 |
} |
|
172 |
} |
|
173 |
}; |
|
174 |
|
|
175 |
// extenders |
|
176 |
|
|
177 |
// adding several properties to target in one extender |
|
178 |
ko.extenders.fibu_sums = function (target, unused) { |
|
179 |
// console.log('called fibu_sums extender'); |
|
180 |
// target is an array of bookings |
|
181 |
|
|
182 |
// debugger; |
|
183 |
// create various sums from bookings |
|
184 |
// assumes booking model contains: |
|
185 |
// * Numbers debit, credit, amount (not observables) |
|
186 |
// * amount as a Number |
|
187 |
// |
|
188 |
target.debitSum = ko.computed(function () { |
|
189 |
return bookings_sum(target, 'debit'); |
|
190 |
}); |
|
191 |
target.creditSum = ko.computed(function () { |
|
192 |
return bookings_sum(target, 'credit'); |
|
193 |
}); |
|
194 |
target.saldo = ko.computed(function () { |
|
195 |
return bookings_sum(target, true); |
|
196 |
}); |
|
197 |
target.saldo_cd = ko.computed(function () { |
|
198 |
return bookings_sum(target, 'cd'); |
|
199 |
}); |
|
200 |
target.hasBookings = ko.computed(function () { |
|
201 |
return target().length > 0; |
|
202 |
}); |
|
203 |
target.isBalanced = ko.computed(function () { |
|
204 |
if ( target().length == 0 ) |
|
205 |
return false; |
|
206 |
return bookings_sum(target, true) == 0 ? true : false; |
|
207 |
}); |
|
208 |
target.hasCreditSaldo = ko.computed(function () { |
|
209 |
return target.saldo() > 0 ? true : false; |
|
210 |
}); |
|
211 |
target.hasDebitSaldo = ko.computed(function () { |
|
212 |
return target.saldo() < 0 ? true : false; |
|
213 |
}); |
|
214 |
|
|
215 |
return target; |
|
216 |
}; |
|
217 |
|
|
218 |
ko.extenders.to_kivitendo = function (target, precision) { |
|
219 |
target.to_kivitendo = ko.computed(function () { |
|
220 |
return target() === null ? undefined : kivi.format_amount(target(), precision); |
|
221 |
}); |
|
222 |
}; |
|
223 |
|
|
224 |
// ko.extenders.to_kivitendo_rewrite = function(target, precision) { |
|
225 |
// //create a writable computed observable to intercept writes to our observable |
|
226 |
// var result = ko.pureComputed({ |
|
227 |
// read: target, |
|
228 |
// write: function(newValue) { |
|
229 |
// var current = target(), |
|
230 |
// newValueAsNum = isNaN( kivi.parse_amount(newValue)) ? 0 : kivi.parse_amount(newValue), |
|
231 |
// valueToWrite = kivi.format_amount(newValueAsNum, precision); |
|
232 |
// //only write if it changed |
|
233 |
// if (valueToWrite !== current) { |
|
234 |
// target(valueToWrite); |
|
235 |
// } else { |
|
236 |
// //if the rounded value is the same, but a different value was written, force a notification for the current field |
|
237 |
// if (newValue !== current) { |
|
238 |
// target.notifySubscribers(valueToWrite); |
|
239 |
// } |
|
240 |
// } |
|
241 |
// } |
|
242 |
// }).extend({ notify: 'always' }); |
|
243 |
// |
|
244 |
// //initialize with current value to make sure it is rounded appropriately |
|
245 |
// result(target()); |
|
246 |
// |
|
247 |
// //return the new computed observable |
|
248 |
// return result; |
|
249 |
// }; |
|
250 |
|
|
251 |
ko.extenders.to_kivitendo_not_zero_reformat = function(target, precision) { |
|
252 |
//create a writable computed observable to intercept writes to our observable |
|
253 |
var result = ko.pureComputed({ |
|
254 |
read: target, //always return the original observables value |
|
255 |
write: function(newValue) { |
|
256 |
<!-- console.log('changing from ' + target() + ' to ' + newValue + ' -> (' + kivi.format_amount(newValue,2) + ')'); --> |
|
257 |
// console.log('changing from ' + target() + ' to ' + newValue); |
|
258 |
var current = target(); |
|
259 |
var formattedNewValue = isNaN(newValue) ? kivi.parse_amount(newValue) : newValue; |
|
260 |
var newValueAsNum = isNaN(formattedNewValue) ? undefined : formattedNewValue; |
|
261 |
var valueToWrite = formattedNewValue ? kivi.format_amount(formattedNewValue, precision) : undefined; |
|
262 |
//only write if it changed |
|
263 |
if (valueToWrite !== current) { |
|
264 |
target(valueToWrite); |
|
265 |
} else { |
|
266 |
//if the rounded value is the same, but a different value was written, force a notification for the current field |
|
267 |
if (newValue !== current) { |
|
268 |
target.notifySubscribers(valueToWrite); |
|
269 |
} |
|
270 |
} |
|
271 |
} |
|
272 |
}).extend({ notify: 'always' }); |
|
273 |
|
|
274 |
//initialize with current value to make sure it is rounded appropriately |
|
275 |
result(target()); |
|
276 |
|
|
277 |
//return the new computed observable |
|
278 |
return result; |
|
279 |
}; |
|
280 |
|
|
281 |
// ko.extenders.to_kivitendo_not_zero = function(target, precision) { |
|
282 |
// //create a writable computed observable to intercept writes to our observable |
|
283 |
// var result = ko.pureComputed({ |
|
284 |
// read: target, //always return the original observables value |
|
285 |
// write: function(newValue) { |
|
286 |
// var current = target(), |
|
287 |
// newValueAsNum = isNaN(newValue) ? undefined : +newValue, |
|
288 |
// valueToWrite = newValueAsNum ? kivi.format_amount(newValueAsNum, precision) : undefined; |
|
289 |
// //only write if it changed |
|
290 |
// if (valueToWrite !== current) { |
|
291 |
// target(valueToWrite); |
|
292 |
// } else { |
|
293 |
// //if the rounded value is the same, but a different value was written, force a notification for the current field |
|
294 |
// if (newValue !== current) { |
|
295 |
// target.notifySubscribers(valueToWrite); |
|
296 |
// } |
|
297 |
// } |
|
298 |
// } |
|
299 |
// }).extend({ notify: 'always' }); |
|
300 |
// |
|
301 |
// //initialize with current value to make sure it is rounded appropriately |
|
302 |
// result(target()); |
|
303 |
// |
|
304 |
// //return the new computed observable |
|
305 |
// return result; |
|
306 |
//}; |
|
307 |
|
|
308 |
ko.extenders.to_amount_cd = function (target, precision) { |
|
309 |
target.to_amount_cd = ko.computed(function () { |
|
310 |
return target() === null ? undefined : format_sh(target()) |
|
311 |
}); |
|
312 |
}; |
|
313 |
|
|
314 |
ko.extenders.formatted = function (underlyingObservable, formatFunction) { |
|
315 |
underlyingObservable.formatted = ko.computed(function () { |
|
316 |
return formatFunction(underlyingObservable()); |
|
317 |
}); |
|
318 |
}; |
|
319 |
|
|
320 |
// helper functions |
|
321 |
|
|
322 |
function bookings_sum (bookings, type) { |
|
323 |
var total = 0; |
|
324 |
ko.utils.arrayForEach(bookings(), function(booking) { |
|
325 |
if ( type === 'credit' ) { |
|
326 |
total += Number(booking.credit); |
|
327 |
} else if ( type === 'debit' ) { |
|
328 |
total += Number(booking.debit); |
|
329 |
} else { |
|
330 |
total += booking.amount; |
|
331 |
} |
|
332 |
}); |
|
333 |
if ( type === 'cd' ) { |
|
334 |
return format_sh(total); |
|
335 |
} else { |
|
336 |
return total.toFixed(5); |
|
337 |
} |
|
338 |
} |
|
339 |
|
|
340 |
function format_sh (val) { |
|
341 |
if ( val === null ) { |
|
342 |
return undefined; |
|
343 |
} else { |
|
344 |
if ( val === 0 ) { |
|
345 |
return kivi.format_amount(val,2); |
|
346 |
} else if ( val < 0 ) { |
|
347 |
return kivi.format_amount(val * (-1),2) + ' S'; |
|
348 |
} else if ( val > 0 ) { |
|
349 |
return kivi.format_amount(val * (1),2) + ' H'; |
|
350 |
} else { |
|
351 |
return val; |
|
352 |
} |
|
353 |
} |
|
354 |
} |
Auch abrufbar als: Unified diff
Konten ausziffern - Knockout Helper Funktionen