Skip to content
Permalink
Newer
Older
100644 687 lines (649 sloc) 27.3 KB
1
// Journal object chooser dialog
2
define(['picoModal','sugar-web/datastore','sugar-web/graphics/icon','mustache','sugar-web/env','sugar-web/graphics/radiobuttonsgroup'], function(picoModal,datastore,icon,mustache,env,radioButtonsGroup) {
3
4
var chooser = {};
5
6
// Load settings
7
var userSettings = null;
8
var activities = [];
9
env.getEnvironment(function(err, environment) {
10
userSettings = environment.user;
11
for (var i = 0 ; userSettings.activities && i < userSettings.activities.length ; i++) {
12
var activity = userSettings.activities[i];
13
activities[activity.id] = activity;
14
}
15
});
16
17
// Chooser feature to search in the Local Journal
18
chooser.featureLocalJournal = {}
19
var featureLocalJournal = chooser.featureLocalJournal;
20
featureLocalJournal.id = "journal-button";
21
featureLocalJournal.title = "$titleJournal";
22
featureLocalJournal.placeholder = "$holderSearchJournal";
23
featureLocalJournal.icon = "lib/sugar-web/graphics/icons/actions/activity-journal.svg";
24
featureLocalJournal.beforeActivate = function() {
25
featureLocalJournal.isFavorite = false;
26
document.getElementById('favorite-button').style.backgroundImage = "url(lib/sugar-web/graphics/icons/emblems/favorite.svg)";
27
journalFill.apply(null, featureLocalJournal.filters);
28
};
29
featureLocalJournal.beforeUnactivate = function() {
30
};
31
featureLocalJournal.onFavorite = function() {
32
var favorite = document.getElementById('favorite-button');
33
if (!featureLocalJournal.isFavorite) {
34
icon.colorize(favorite, userSettings.colorvalue, function() {});
35
} else {
36
favorite.style.backgroundImage = "url(lib/sugar-web/graphics/icons/emblems/favorite.svg)";
37
}
38
featureLocalJournal.isFavorite = !featureLocalJournal.isFavorite;
39
journalFill.apply(null, featureLocalJournal.filters);
40
};
41
featureLocalJournal.onScroll = function() {};
42
featureLocalJournal.onSearch = function() {
43
journalFill.apply(null, featureLocalJournal.filters);
44
};
45
featureLocalJournal.onCancelSearch = function() {
46
journalFill.apply(null, featureLocalJournal.filters);
47
};
48
49
// Chooser feature to search in Abecedarium database
50
var featureAbecedarium = {};
51
featureAbecedarium.id = "abecedarium-button";
52
featureAbecedarium.title = "$titleAbecedarium";
53
featureAbecedarium.placeholder = "$holderSearchAbecedarium";
54
featureAbecedarium.icon = "lib/sugar-web/graphics/icons/actions/activity-abecedarium.svg";
55
featureAbecedarium.beforeActivate = function() {
56
document.getElementById('favorite-button').style.visibility = "hidden";
57
abecedariumInit(abecedariumFill);
58
};
59
featureAbecedarium.beforeUnactivate = function() {
60
document.getElementById('favorite-button').style.visibility = "visible";
61
};
62
featureAbecedarium.onFavorite = function() {};
63
featureAbecedarium.onScroll = function() {
64
var container = document.getElementById('journal-container');
65
var scrollValue = (container.scrollTop) / (container.scrollHeight - container.clientHeight);
66
if (scrollValue > 0.90) {
67
featureAbecedarium.resultCount += 30;
68
abecedariumDisplay(featureAbecedarium.results, featureAbecedarium.resultCount);
69
}
70
};
71
featureAbecedarium.onSearch = function() {
72
abecedariumFill();
73
};
74
featureAbecedarium.onCancelSearch = function() {
75
abecedariumFill();
76
};
77
78
// Chooser feature to take a screen capture
79
chooser.featurePhoto = {}
80
var featurePhoto = chooser.featurePhoto;
81
featurePhoto.id = "photo-button";
82
featurePhoto.title = "$titlePhoto";
83
featurePhoto.placeholder = "";
84
featurePhoto.icon = "lib/sugar-web/graphics/icons/actions/photo.svg";
85
featurePhoto.beforeActivate = function() {
86
var captureSuccess = function (imageData) {
87
var data = "data:image/jpeg;base64," + imageData;
88
featurePhoto.createJournalEntry(data, function() {
89
modal.close();
90
});
91
};
92
var captureError = function (error) {
93
modal.close();
94
};
95
if (window.cordova || window.PhoneGap) {
96
navigator.camera.getPicture(captureSuccess, captureError, {
97
quality: 50,
98
targetWidth: 640,
99
targetHeight: 480,
100
destinationType: Camera.DestinationType.DATA_URL,
101
sourceType: Camera.PictureSourceType.CAMERA
102
});
103
} else {
104
try {
105
var video = document.createElement("video");
106
video.id = "videocapture";
107
video.style.zIndex = "300";
108
video.setAttribute("controls", false);
109
video.setAttribute("autoplay", "true");
110
video.style.width = "100%";
111
video.style.top = "0px";
112
video.style.position = "absolute";
113
var parent = document.getElementById("journal-container");
114
parent.appendChild(video);
115
navigator.mediaDevices.getUserMedia({video: true}).then(function(mediaStream) {
116
video.srcObject = mediaStream;
117
setTimeout(function () {
118
var canvas = document.createElement("canvas");
119
var width = 320;
120
var height = 240;
121
canvas.width = width;
122
canvas.height = height;
123
canvas.getContext('2d').drawImage(video, 0, 0, width, height);
124
var data = canvas.toDataURL("image/png");
125
featurePhoto.createJournalEntry(data, function() {
126
if (modal.isVisible()) { modal.close(); }
127
mediaStream.getTracks().forEach(function(track) { track.stop(); });
128
parent.removeChild(video);
129
});
130
}, 2700);
131
}).catch(function (error) {
132
modal.close();
133
parent.removeChild(video);
134
});
135
} catch (e) {
136
modal.close();
137
}
138
}
139
};
140
featurePhoto.beforeUnactivate = function() {};
141
featurePhoto.onFavorite = function() {};
142
featurePhoto.onScroll = function() {};
143
featurePhoto.onSearch = function() {};
144
featurePhoto.onCancelSearch = function() {};
145
featurePhoto.createJournalEntry = function(data, callback) {
146
var metadata = {
147
mimetype: "image/jpeg",
148
title: doLocalize("$photoName", {name:userSettings.name}),
149
activity: "org.olpcfrance.MediaViewerActivity",
150
timestamp: new Date().getTime(),
151
creation_time: new Date().getTime(),
152
file_size: 0
153
};
154
datastore.create(metadata, function(err, objectId) {
155
if (err == null) { result = {metadata:metadata, objectId: objectId}; }
156
if (callback) { callback(); }
157
}, data);
158
},
159
160
// Init feature list: overload it if you want to change the feature list at init
161
chooser.features = [];
162
chooser.currentFeature = -1;
163
chooser.init = function() {
164
chooser.features = [featureLocalJournal];
165
chooser.currentFeature = 0;
167
168
// Display object chooser dialog with journal content
169
// Each filter has the form = {title: "%hysics", creation_time: "<12222", activity: ["org.olpcfrance.Paint","org.olpcfrance.Record"]}
170
// When more than one filter is provided, an entry is valid if it match one filter or more. Usage examples:
171
// chooser.show();
172
// chooser.show({title: 'Paint Activity', creation_time: '>'+now.getTime()});
173
// chooser.show({title: ['Paint Activity', '%hysics']});
174
// chooser.show({keep: 1});
175
// chooser.show({activity: 'org.olpcfrance.PaintActivity'}, {mimetype: 'image/png'})
176
var modal;
177
var result;
178
chooser.show = function(callback, filter1, orFilter2, orFilter3, orFilter4) {
179
result = null;
180
chooser.init();
181
var imageType = "image/png";
February 13, 2020 21:46
182
var soundType = "audio/mpeg";
183
var filters = [filter1, orFilter2, orFilter3, orFilter4];
184
for (var i = 0 ; i < filters.length ; i++) {
185
if (filters[i]) {
186
if (filters[i].mimetype == imageType) {
187
featureAbecedarium.fileformat = ".png";
188
featureAbecedarium.mimetype = imageType;
189
featureAbecedarium.filelocation = "images/database/";
190
chooser.features.push(featureAbecedarium);
191
chooser.features.push(featurePhoto);
192
break;
193
} else if (filters[i].mimetype == soundType) {
February 13, 2020 21:46
194
featureAbecedarium.fileformat = ".mp3";
195
featureAbecedarium.mimetype = soundType;
196
featureAbecedarium.filelocation = "audio/{{lang}}/database/";
197
chooser.features.push(featureAbecedarium);
198
break;
199
}
200
}
201
}
202
var contentHeader = "<div id='pictotoolbar' class='toolbar' style='padding: 0'>";
203
for (var i = 0 ; i < chooser.features.length ; i++) {
204
contentHeader += "<button class='toolbutton"+(i==0?" active":"")+"' id='"+chooser.features[i].id+"' title='"+chooser.features[i].title+"' style='background-image: url("+chooser.features[i].icon+")'></button>";
205
}
206
contentHeader += "<button class='toolbutton pull-right' id='close-button' title='$titleClose' style='background-image: url(lib/sugar-web/graphics/icons/actions/dialog-cancel.svg)'></button>";
207
contentHeader += "<div style='position: absolute; display: inline-block; margin-left: 10px; top: 20px; height:55px'>$titleChoose</div></div>";
208
modal = picoModal({
209
content: doLocalize(contentHeader+"\
210
<div class='toolbar' style='border-top-style: solid; border-color: #c0c0c0; border-width: 1px'>\
211
<span class='icon-input' style='vertical-align:top;'>\
212
<input id='search-text' type='text' style='width: 200px; font-size: 10pt'/>\
213
<button id='cancel-search' class='cancel right'></button>\
214
</span>\
215
<button class='toolbutton' id='favorite-button' title='$titleFavorite' style='background-image: url(lib/sugar-web/graphics/icons/emblems/favorite.svg)'></button>\
216
</div>\
217
<div id='journal-empty' style='position:absolute;width:100%;top:50%;left:50%'>\
218
<div style='width:60px;height:60px;background-repeat: no-repeat;background-image: url(lib/sugar-web/graphics/icons/actions/activity-journal.svg)'></div>\
219
<div style='text-align:left!important;margin-left:-30px;color:#808080;text-align:center;font-size:14px;'>$noMatchingEntries</div>\
220
</div>\
221
<div id='journal-container' style='width: 100%; overflow:auto'></div>"),
222
closeButton: false,
223
modalStyles: {
224
backgroundColor: "white",
225
height: "400px",
226
width: "600px",
227
maxWidth: "90%"
228
}
229
})
230
.afterShow(function(modal) {
231
var color = userSettings.colorvalue;
232
icon.colorize(document.getElementById(chooser.features[chooser.currentFeature].id), color, function() {});
233
var radios = [];
234
for (var i = 0 ; i < chooser.features.length ; i++) {
235
var radio = document.getElementById(chooser.features[i].id);
236
radios.push(radio);
237
radio.addEventListener("click", function(e) {
238
var index = -1;
239
for (var j = 0 ; j < chooser.features.length ; j++) {
240
if (chooser.features[j].id == e.srcElement.id) {
241
index = j;
242
break;
243
}
244
}
245
if (index != chooser.currentFeature) {
246
chooser.features[chooser.currentFeature].beforeUnactivate();
247
document.getElementById(chooser.features[chooser.currentFeature].id).style.backgroundImage = "url("+chooser.features[chooser.currentFeature].icon+")";
248
document.getElementById('journal-container').innerHTML = "";
249
document.getElementById('search-text').value = '';
250
chooser.currentFeature = index;
251
chooser.features[chooser.currentFeature].filters = [filter1, orFilter2, orFilter3, orFilter4];
252
chooser.features[chooser.currentFeature].beforeActivate();
253
icon.colorize(document.getElementById(chooser.features[chooser.currentFeature].id), color, function() {});
254
document.getElementById('search-text').placeholder=doLocalize(chooser.features[chooser.currentFeature].placeholder);
255
}
256
})
257
}
258
new radioButtonsGroup.RadioButtonsGroup(radios);
259
260
var targetHeight = document.getElementById("pictotoolbar").parentNode.offsetHeight - 55*2;
261
document.getElementById('journal-container').style.height = targetHeight + "px";
262
document.getElementById('journal-container').addEventListener("scroll", function() {
263
chooser.features[chooser.currentFeature].onScroll();
265
var favorite = document.getElementById('favorite-button');
266
favorite.addEventListener('click', function() {
267
chooser.features[chooser.currentFeature].onFavorite();
268
});
269
document.getElementById('search-text').addEventListener('keyup', function() {
270
chooser.features[chooser.currentFeature].onSearch();
271
});
272
document.getElementById('cancel-search').addEventListener('click', function() {
273
document.getElementById('search-text').value = '';
274
chooser.features[chooser.currentFeature].onCancelSearch();
275
});
276
document.getElementById('close-button').addEventListener('click', function() {
277
result = null;
278
modal.close();
279
});
280
281
document.getElementById('search-text').placeholder=doLocalize(chooser.features[chooser.currentFeature].placeholder);
282
chooser.features[chooser.currentFeature].filters = [filter1, orFilter2, orFilter3, orFilter4];
283
chooser.features[chooser.currentFeature].beforeActivate();
284
})
285
.afterClose(function(modal) {
286
modal.destroy();
287
if (callback) {
288
callback(result);
289
}
290
})
291
.show();
292
}
293
294
// --- Journal feature functions
295
296
// Get journal entries and fill dialog with entries
297
function journalFill(filter1, orFilter2, orFilter3, orFilter4) {
298
// Get filters
299
var filters = [];
300
if (filter1) { filters.push(filter1); }
301
if (orFilter2) { filters.push(orFilter2); }
302
if (orFilter3) { filters.push(orFilter3); }
303
if (orFilter4) { filters.push(orFilter4); }
304
305
// Compute filtering
306
var journal = datastore.find();
307
var rawJournal = [];
308
for (var i = 0 ; i < journal.length ; i++) {
309
var entry = journal[i];
310
var match = true;
311
if (featureLocalJournal.isFavorite) {
312
match = match && entry.metadata.keep;
313
}
314
var title = document.getElementById('search-text').value;
315
if (title && title.length > 0) {
316
match = match && (entry.metadata.title && (entry.metadata.title.indexOf(title) != -1));
317
}
318
if (match) {
319
rawJournal.push(entry)
320
}
321
}
322
var length = filters.length;
323
var filteredJournal = rawJournal;
324
if (length > 0) {
325
filteredJournal = [];
326
for(var i = 0 ; i < rawJournal.length ; i++) {
327
var entry = rawJournal[i];
328
var match = false;
329
for (var j = 0 ; j < length ; j++) {
330
match = match || journalFilterMatch(entry, filters[j]);
331
}
332
if (match) {
333
filteredJournal.push(entry);
334
}
335
}
336
}
337
338
// Get entries
339
var journal = {entries: filteredJournal.sort(function(e0, e1) {
340
return parseInt(e1.metadata.timestamp) - parseInt(e0.metadata.timestamp);
341
})};
342
343
// Add style properties
344
for (var i = 0 ; i < journal.entries.length ; i++) {
345
var entry = journal.entries[i];
346
var activity = activities[entry.metadata.activity];
347
entry.imageUrl = "../../" + activity.directory + "/" + activity.icon;
348
entry.index = i;
349
entry.ago = timestampToElapsedString(entry.metadata.creation_time);
350
}
351
352
// Draw it
353
var template = "\
354
{{#entries}}\
355
<div id='entry_{{index}}' style='height:60px'>\
356
<div id='eicon_{{index}}' class='toolbutton' style='background-image:url({{imageUrl}});background-size:40px 40px;width:40px;height:40px;display:inline-block;margin-left:20px;margin-top:5px;'></div>\
357
<div id='etext_{{index}}' style='color:black;display:inline-block;vertical-align:middle;margin-left:30px;height:60px;margin-top:10px;font-weight:bold;font-size:14px;'>{{metadata.title}}</div>\
358
<div id='edate_{{index}}' style='color:black;vertical-align:baseline;text-align:right;float:right;height:45px;padding-top:15px;margin-right:10px;clear:right;font-weight:normal;font-size:14px;'>{{ago}}</div>\
359
</div>\
360
{{/entries}}\
361
";
362
var render = mustache.render(template, journal);
363
document.getElementById('journal-container').innerHTML = render;
364
365
// Handle click
366
for (var i = 0 ; i < journal.entries.length ; i++) {
367
var entry = document.getElementById('entry_'+i);
368
entry.addEventListener('click', function(e) {
369
var id = e.target.id;
370
id = id.substr(id.indexOf("_")+1);
371
var line = document.getElementById('entry_'+id);
372
line.style.backgroundColor = "#808080";
373
result = journal.entries[id];
374
delete result['ago'];
375
delete result['index'];
376
delete result['imageUrl'];
377
window.setTimeout(function() {
378
modal.close(result);
379
}, 200);
380
});
381
}
382
document.getElementById('journal-empty').style.visibility = (journal.entries.length != 0 ? 'hidden' : 'visible');
383
}
384
385
// Test if an entry match a value
386
function journalEntryMatch(entry, name, value) {
387
var fieldValue = entry[name];
388
if (!fieldValue) { return false; }
389
var firstChar = value[0];
390
if (firstChar == '%') {
391
return fieldValue.indexOf(value.substr(1)) != -1;
392
} else if (firstChar == '>') {
393
return parseInt(fieldValue) > parseInt(value.substr(1));
394
} else if (firstChar == '<') {
395
return parseInt(fieldValue) < parseInt(value.substr(1));
396
} else {
397
return fieldValue == value;
398
}
399
}
400
401
// Test if an entry match a filter
402
function journalFilterMatch(entry, filter) {
403
var metadata = entry.metadata;
404
var keys = Object.keys(filter);
405
var match = true;
406
for (var j = 0 ; j < keys.length ; j++) {
407
var field = keys[j];
408
var value = filter[field];
409
if (value instanceof Array) {
410
var or = false;
411
for (var k = 0 ; k < value.length ; k++) {
412
or = or || journalEntryMatch(metadata, field, value[k]);
413
}
414
match = match && or;
415
} else {
416
match = match && journalEntryMatch(metadata, field, value);
417
}
418
}
419
return match;
420
}
421
422
// --- Abecedarium feature functions
423
424
// Load Abecedarium database
425
function abecedariumInit(callback) {
426
featureAbecedarium.database = {};
427
document.getElementById('journal-empty').style.visibility = 'visible';
428
featureAbecedarium.baseURL = document.location.href.substr(0, document.location.href.indexOf("/activities/"))+"/activities/Abecedarium.activity/";
429
featureAbecedarium.lang = (["en","es","fr"].indexOf(userSettings.language)!=-1)?userSettings.language:"en";
430
featureAbecedarium.filelocation = featureAbecedarium.filelocation.replace("{{lang}}",featureAbecedarium.lang);
February 13, 2020 21:46
431
var loadDatabase = function(file, entry, subcallback) {
432
var client = new XMLHttpRequest();
433
var source = featureAbecedarium.baseURL+file;
434
client.onload = function() {
435
if (entry == "ping") {
436
featureAbecedarium.database[entry]=(this.status == 0 || (this.status >= 200 && this.status < 300));
February 13, 2020 21:46
437
} else if (this.status == 0 || (this.status >= 200 && this.status < 300)) {
438
featureAbecedarium.database[entry]=JSON.parse(this.responseText);
February 13, 2020 21:46
439
}
440
subcallback();
441
};
442
client.onerror = function() {
443
if (entry == "ping") {
444
featureAbecedarium.database[entry]=false;
445
subcallback();
446
}
447
}
February 13, 2020 21:46
448
client.open("GET", source);
449
client.send();
450
};
451
loadDatabase("database/db_url.json", "url", function() {
452
loadDatabase("database/db_meta.json", "meta", function() {
453
loadDatabase("database/db_"+featureAbecedarium.lang+".json", "words", function() {
454
loadDatabase("images/database/_ping.png?"+(new Date()).getTime(), "ping", function() {
455
var len = featureAbecedarium.database.meta.length;
456
var entries = [];
457
for (var i = 0 ; i < len; i++) {
458
if (featureAbecedarium.database.meta[i][featureAbecedarium.lang]) {
459
entries.push({"code":featureAbecedarium.database.meta[i]["code"],"text":featureAbecedarium.database.words[featureAbecedarium.database.meta[i]["text"]]});
460
}
461
}
462
entries.sort(function(a,b) {
463
return a.text.localeCompare(b.text, 'en', {sensitivity: 'base'});
464
});
465
var sortedEntries = [];
466
for (var i = 0 ; i < entries.length ; i++) {
467
entries[i].i = sortedEntries.length;
468
sortedEntries.push(entries[i]);
469
}
470
featureAbecedarium.database.content = sortedEntries;
471
delete featureAbecedarium.database.meta;
472
delete featureAbecedarium.database.words;
473
callback();
February 13, 2020 21:46
474
});
475
});
476
});
477
});
478
}
479
480
// Fill popup with Abecedarium items
481
function abecedariumFill() {
482
// Find entries matching search field
483
var content = featureAbecedarium.database.content;
484
var title = document.getElementById('search-text').value.toLowerCase();
485
if (title.length) {
486
content = [];
487
for (var i = 0 ; i < featureAbecedarium.database.content.length ; i++) {
488
if (featureAbecedarium.database.content[i].text.toLowerCase().indexOf(title) != -1) {
489
content.push(featureAbecedarium.database.content[i]);
490
}
491
}
492
}
493
494
// Display result
495
featureAbecedarium.results = content;
496
featureAbecedarium.resultCount = 30;
497
abecedariumDisplay(content, featureAbecedarium.resultCount);
498
}
499
500
function abecedariumDisplay(content, count) {
501
// Display entries
502
var template = "\
503
{{#items}}\
504
<div id='entry_{{i}}' style='height:60px'>\
505
<div id='eicon_{{i}}' class='toolbutton' style='background-image:url(../../activities/MediaViewer.activity/activity/activity-icon.svg);background-size:40px 40px;width:40px;height:40px;display:inline-block;margin-left:20px;margin-top:5px;'></div>\
506
<div id='etext_{{i}}' style='color:black;display:inline-block;vertical-align:middle;margin-left:30px;height:60px;margin-top:10px;font-weight:bold;font-size:14px;'>{{text}}</div>\
507
</div>\
508
{{/items}}\
509
";
510
var items = {items: content.slice(0, count)};
511
var render = mustache.render(template, items);
512
document.getElementById('journal-empty').style.visibility = (content.length != 0 ? 'hidden' : 'visible');
513
document.getElementById('journal-container').innerHTML = render;
514
515
// Handle click
516
var len = Math.min(count, content.length);
517
for (var i = 0 ; i < len; i++) {
518
var entry = document.getElementById('entry_'+content[i].i);
519
entry.addEventListener('click', function(e) {
520
var id = e.target.id;
521
id = id.substr(id.indexOf("_")+1);
522
var line = document.getElementById('entry_'+id);
523
line.style.backgroundColor = "#808080";
524
abecedariumCreateEntry(featureAbecedarium.database.content[id], function() {
525
modal.close(result);
526
});
527
});
528
if (featureAbecedarium.mimetype == "image/png") {
529
document.getElementById('eicon_'+content[i].i).style.backgroundImage="url("+(featureAbecedarium.database.ping?featureAbecedarium.baseURL:featureAbecedarium.database.url)+"images/database/"+content[i].code+".png"+")";
530
}
531
}
532
}
533
534
// Create a record in Journal for the entry
535
function abecedariumCreateEntry(entry, callback) {
536
var url = featureAbecedarium.database.ping?featureAbecedarium.baseURL:featureAbecedarium.database.url;
537
var mimetype = featureAbecedarium.mimetype;
538
var request = new XMLHttpRequest();
539
request.open("GET",url+featureAbecedarium.filelocation+entry.code+featureAbecedarium.fileformat,true);
540
request.setRequestHeader("Content-type",mimetype);
541
request.responseType = "arraybuffer";
542
var that = this;
543
request.onload = function() {
544
if (request.status == 200 || request.status == 0) {
545
var blob = new Uint8Array(this.response);
546
var base64 = "data:"+mimetype+";base64,"+toBase64(blob);
547
var metadata = {
548
mimetype: mimetype,
549
title: entry.text+featureAbecedarium.fileformat,
550
activity: "org.olpcfrance.MediaViewerActivity",
551
timestamp: new Date().getTime(),
552
creation_time: new Date().getTime(),
553
file_size: 0
554
};
555
datastore.create(metadata, function(err, objectId) {
556
console.log("Entry '"+entry.text+"' saved in journal.");
557
result = {metadata:metadata, objectId: objectId};
558
callback();
559
}, base64);
560
} else {
561
console.log("Error loading entry '"+entry.code+"'.");
562
callback();
563
}
564
};
565
request.onerror = function() {
566
console.log("Error loading entry '"+entry.code+"'.");
567
callback();
568
};
569
request.send();
570
}
571
572
// --- Utility functions
573
574
// Localize content - currently means only localize in English
575
var l10n = {
576
titleJournal: {en: 'Journal', fr: 'Journal', es: 'Diario', pt: 'Diário'},
577
titleAbecedarium: {en: 'Abecedarium', fr: 'Abecedarium', es: 'Abecedarium', pt: 'Abecedarium'},
578
titlePhoto: {en: 'Photo', fr: 'Photo', es: 'Photo', pt: 'Photo'},
579
titleClose: {en: 'Cancel', fr: 'Annuler', es: 'Cancelar', pt: 'Cancelar'},
580
titleChoose: {en: 'Choose an object', fr: 'Choisir un objet', es: 'Elige un objeto', pt: 'Escolher um objeto'},
581
photoName: {en: 'Photo by {{name}}', fr: 'Photo par {{name}}', es: 'Photo por {{name}}', pt: 'Photo por {{name}}'},
582
holderSearchJournal: {en: 'Search in Journal', fr: 'Recherche dans le journal', es: 'Buscar en el diario', pt: 'Pesquisar no diário'},
583
holderSearchAbecedarium: {en: 'Search in Abecedarium', fr: 'Recherche dans Abecedarium', es: 'Buscar en Abecedarium', pt: 'Pesquisar no Abecedarium'},
584
noMatchingEntries: {en: 'No matching entries', fr: 'Aucune entrée correspondante', es: 'No hay actividades coincidentes', pt: 'Sem atividades correspondentes'},
585
SecondsAgo: {en: 'Seconds ago', fr: "A l'instant", es: 'Segundos atrás', pt: 'Segundos atrás'},
586
Ago: {en: '{{time}} ago', fr: 'il y a {{time}}', es: '{{time}} atrás', pt: '{{time}} atrás'},
587
Minutes_one: {en: 'minute', fr: 'minute', es: 'minuto', pt: 'minuto'},
588
Minutes_other: {en: 'minutes', fr: 'minutes', es: 'minutos', pt: 'minutos'},
589
Hours_one: {en: 'hour', fr: 'heure', es: 'hora', pt: 'hora'},
590
Hours_other: {en: 'hours', fr: 'heures', es: 'horas', pt: 'horas'},
591
Days_one: {en: 'day', fr: 'jour', es: 'día', pt: 'dia'},
592
Days_other: {en: 'days', fr: 'jours', es: 'días', pt: 'dias'},
593
Weeks_one: {en: 'week', fr: 'semaine', es: 'semana', pt: 'semana'},
594
Weeks_other: {en: 'weeks', fr: 'semaines', es: 'semanas', pt: 'semanas'},
595
Months_one: {en: 'month', fr: 'mois', es: 'mes', pt: 'mês'},
596
Months_other: {en: 'months', fr: 'mois', es: 'meses', pt: 'meses'},
597
Years_one: {en: 'year', fr: 'année', es: 'año', pt: 'ano'},
598
Years_other: {en: 'years', fr: 'années', es: 'años', pt: 'anos'},
599
};
600
function doLocalize(str, params) {
601
var lang = (["en","fr","es", "pt"].indexOf(userSettings.language)!=-1)?userSettings.language:"en";
602
var out = str;
603
var variablesToReplace = out.match(/\$\w+/g);
604
for(var i in variablesToReplace) {
605
var string = variablesToReplace[i].match(/\w+/)[0];
606
if(l10n[string]) {
607
out = out.replace(variablesToReplace[i], l10n[string][lang]);
608
}
610
var paramsInString = out.match(/{{\s*[\w\.]+\s*}}/g);
611
for(var i in paramsInString) {
612
var param = paramsInString[i].match(/[\w\.]+/)[0];
613
out = out.replace(paramsInString[i], params[param]);
614
}
615
return out;
616
}
617
618
// Compute elapsed time as a string
619
function timestampToElapsedString(timestamp) {
620
var units = [{name:'Years', factor:356 * 24 * 60 * 60},
621
{name:'Months', factor:30 * 24 * 60 * 60},
622
{name:'Weeks', factor:7 * 24 * 60 * 60},
623
{name:'Days', factor:24 * 60 * 60},
624
{name:'Hours', factor:60 * 60},
625
{name:'Minutes', factor:60}];
626
var maxlevel = 1;
627
var levels = 0;
628
var time_period = '';
629
var time_stamp = (new Date(timestamp).getTime());
630
var elapsed_seconds = ((new Date().getTime()) - time_stamp)/1000;
631
for (var i = 0; i < units.length ; i++) {
632
var factor = units[i].factor;
633
634
var elapsed_units = Math.floor(elapsed_seconds / factor);
635
if (elapsed_units > 0) {
636
if (levels > 0)
637
time_period += ',';
638
639
time_period += ' '+elapsed_units+" "+(elapsed_units==1?doLocalize("$"+units[i].name+"_one"):doLocalize("$"+units[i].name+"_other"));
640
641
elapsed_seconds -= elapsed_units * factor;
642
}
643
644
if (time_period != '')
645
levels += 1;
646
647
if (levels == maxlevel)
648
break;
649
}
650
651
if (levels == 0) {
652
return doLocalize("$SecondsAgo");
653
}
654
655
return doLocalize("$Ago", {time: time_period});
656
}
657
658
// Encoding functions taken from
659
// https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
660
function uint6ToB64(nUint6) {
661
return nUint6 < 26 ?
662
nUint6 + 65 : nUint6 < 52 ?
663
nUint6 + 71 : nUint6 < 62 ?
664
nUint6 - 4 : nUint6 === 62 ?
665
43 : nUint6 === 63 ?
666
47 : 65;
667
}
668
function toBase64(aBytes) {
669
var eqLen = (3 - (aBytes.length % 3)) % 3, sB64Enc = "";
670
for (var nMod3, nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) {
671
nMod3 = nIdx % 3;
672
nUint24 |= aBytes[nIdx] << (16 >>> nMod3 & 24);
673
if (nMod3 === 2 || aBytes.length - nIdx === 1) {
674
sB64Enc += String.fromCharCode(uint6ToB64(nUint24 >>> 18 & 63), uint6ToB64(nUint24 >>> 12 & 63), uint6ToB64(nUint24 >>> 6 & 63), uint6ToB64(nUint24 & 63));
675
nUint24 = 0;
676
}
677
}
678
return eqLen === 0 ? sB64Enc : sB64Enc.substring(0, sB64Enc.length - eqLen) + (eqLen === 1 ? "=" : "==");
679
}
680
681
chooser.close = function (resultObj) {
682
result = resultObj;
683
modal.close(result);
684
}
685
686
return chooser;
687
});