not really known
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

793 lines
26 KiB

  1. // Utility functions
  2. define(["webL10n","sugar-web/datastore","FileSaver"], function (l10n, datastore, FileSaver) {
  3. var util = {};
  4. var units = [{name:'Years', factor:356 * 24 * 60 * 60},
  5. {name:'Months', factor:30 * 24 * 60 * 60},
  6. {name:'Weeks', factor:7 * 24 * 60 * 60},
  7. {name:'Days', factor:24 * 60 * 60},
  8. {name:'Hours', factor:60 * 60},
  9. {name:'Minutes', factor:60}];
  10. // Port of timestamp elapsed string from Sugar - timestamp is in milliseconds elapsed since 1er january 1970
  11. util.timestampToElapsedString = function(timestamp, maxlevel, issmall) {
  12. var suffix = (issmall ? "_short" : "");
  13. var levels = 0;
  14. var time_period = '';
  15. var elapsed_seconds = ((new Date().getTime()) - timestamp)/1000;
  16. for (var i = 0; i < units.length ; i++) {
  17. var factor = units[i].factor;
  18. var elapsed_units = Math.floor(elapsed_seconds / factor);
  19. if (elapsed_units > 0) {
  20. if (levels > 0)
  21. time_period += ',';
  22. time_period += ' '+elapsed_units+" "+(elapsed_units==1?l10n.get(units[i].name+"_one"+suffix):l10n.get(units[i].name+"_other"+suffix));
  23. elapsed_seconds -= elapsed_units * factor;
  24. }
  25. if (time_period != '')
  26. levels += 1;
  27. if (levels == maxlevel)
  28. break;
  29. }
  30. if (levels == 0) {
  31. return l10n.get("SecondsAgo"+suffix);
  32. }
  33. return l10n.get("Ago"+suffix, {time:time_period});
  34. }
  35. // Port of get_date_range from Sugar
  36. util.getDateRange = function(rangetype) {
  37. if (rangetype === undefined) {
  38. return null;
  39. }
  40. var now = new Date();
  41. var today = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
  42. now = now.getTime();
  43. var range = null;
  44. switch(rangetype) {
  45. case 1:
  46. range = { min: today, max: now }; // TODAY
  47. break;
  48. case 2:
  49. range = { min: today-86400000, max: now }; // YESTERDAY
  50. break;
  51. case 3:
  52. range = { min: today-604800000, max: now }; // PAST WEEK
  53. break;
  54. case 4:
  55. range = { min: today-2592000000, max: now }; // PAST MONTH
  56. break;
  57. case 5:
  58. range = { min: today-30758400000, max: now }; // PAST YEAR
  59. break;
  60. }
  61. return range;
  62. }
  63. // Port of get_next/previous_stroke/fill_color from Sugar
  64. util.getNextStrokeColor = function(color) {
  65. var current = util.getColorIndex(color);
  66. if (current == -1) {
  67. return color;
  68. }
  69. var next = nextIndex(current);
  70. while (xoPalette.colors[next].stroke != xoPalette.colors[current].stroke) {
  71. next = nextIndex(next);
  72. }
  73. return xoPalette.colors[next];
  74. }
  75. util.getPreviousStrokeColor = function(color) {
  76. var current = util.getColorIndex(color);
  77. if (current == -1) {
  78. return color;
  79. }
  80. var previous = previousIndex(current);
  81. while (xoPalette.colors[previous].stroke != xoPalette.colors[current].stroke) {
  82. previous = previousIndex(previous);
  83. }
  84. return xoPalette.colors[previous];
  85. }
  86. util.getNextFillColor = function(color) {
  87. var current = util.getColorIndex(color);
  88. if (current == -1) {
  89. return color;
  90. }
  91. var next = nextIndex(current);
  92. while (xoPalette.colors[next].fill != xoPalette.colors[current].fill) {
  93. next = nextIndex(next);
  94. }
  95. return xoPalette.colors[next];
  96. }
  97. util.getPreviousFillColor = function(color) {
  98. var current = util.getColorIndex(color);
  99. if (current == -1) {
  100. return color;
  101. }
  102. var previous = previousIndex(current);
  103. while (xoPalette.colors[previous].fill != xoPalette.colors[current].fill) {
  104. previous = previousIndex(previous);
  105. }
  106. return xoPalette.colors[previous];
  107. }
  108. util.getColorIndex = function(color) {
  109. for (var i = 0 ; i < xoPalette.colors.length ; i++) {
  110. if (color.stroke == xoPalette.colors[i].stroke && color.fill == xoPalette.colors[i].fill) {
  111. return i;
  112. }
  113. }
  114. return -1;
  115. }
  116. function nextIndex(index) {
  117. var next = index + 1;
  118. if (next == xoPalette.colors.length) {
  119. next = 0;
  120. }
  121. return next;
  122. }
  123. function previousIndex(index) {
  124. var previous = index - 1;
  125. if (previous < 0) {
  126. previous = xoPalette.colors.length - 1;
  127. }
  128. return previous;
  129. }
  130. // Get center of screen in canvas
  131. util.getCanvasCenter = function() {
  132. var canvas = document.getElementById("canvas") || document.getElementById("body");
  133. var canvas_height = canvas.offsetHeight;
  134. var canvas_width = canvas.offsetWidth;
  135. var canvas_centery = parseFloat(canvas_height)/2.0;
  136. var canvas_centerx = parseFloat(canvas_width)/2.0
  137. return { x: canvas_centerx, y: canvas_centery, dx: canvas_width, dy: canvas_height };
  138. }
  139. // Compute margin for the element to be centered
  140. util.computeMargin = function(size, maxpercent) {
  141. var canvas = document.getElementById("canvas");
  142. var canvas_height = canvas.offsetHeight;
  143. var canvas_width = canvas.offsetWidth;
  144. var size_width = (size.width <= canvas_width ? size.width : maxpercent.width*canvas_width);
  145. var size_height = (size.height <= canvas_height ? size.height : maxpercent.height*canvas_height);
  146. return { left: parseFloat(canvas_width-size_width)/2.0, top: parseFloat(canvas_height-size_height)/2.0, width: size_width, height: size_height };
  147. }
  148. // Test if cursor is inside element
  149. util.cursorIsInside = function(ctrl) {
  150. var obj = document.getElementById(ctrl.getAttribute("id"));
  151. if (obj == null) return false;
  152. var p = {};
  153. p.x = obj.offsetLeft;
  154. p.y = obj.offsetTop;
  155. p.dx = obj.clientWidth;
  156. p.dy = obj.clientHeight;
  157. while (obj.offsetParent) {
  158. p.x = p.x + obj.offsetParent.offsetLeft;
  159. p.y = p.y + obj.offsetParent.offsetTop - obj.scrollTop;
  160. if (obj == document.getElementsByTagName("body")[0]) {
  161. break;
  162. } else {
  163. obj = obj.offsetParent;
  164. }
  165. }
  166. var isInside = (
  167. mouse.position.x >= p.x && mouse.position.x <= p.x + p.dx
  168. && mouse.position.y >= p.y && mouse.position.y <= p.y + p.dy
  169. );
  170. return isInside;
  171. };
  172. // Show/Hide toolbar
  173. util.setToolbar = function(newtoolbar) {
  174. if (toolbar == newtoolbar) {
  175. return;
  176. }
  177. toolbar = newtoolbar;
  178. newtoolbar.renderInto(document.getElementById("toolbar"));
  179. }
  180. // Get browser name
  181. util.getBrowserName = function() {
  182. if (enyo.platform.android) return "Android";
  183. else if (enyo.platform.androidChrome) return "Chrome Android";
  184. else if (enyo.platform.androidFirefox) return "Firefox Android";
  185. else if (enyo.platform.ie) return "IE";
  186. else if (enyo.platform.ios) return "iOS";
  187. else if (enyo.platform.webos) return "webOS";
  188. else if (enyo.platform.blackberry) return "BlackBerry";
  189. else if (enyo.platform.safari) return "Safari";
  190. else if (enyo.platform.chrome) return "Chrome";
  191. else if (enyo.platform.firefox) return "Firefox";
  192. return "Unknown";
  193. }
  194. // Get browser version
  195. util.getBrowserVersion = function() {
  196. if (enyo.platform.android) return enyo.platform.android;
  197. else if (enyo.platform.androidChrome) return enyo.platform.androidChrome;
  198. else if (enyo.platform.androidFirefox) return enyo.platform.androidFirefox;
  199. else if (enyo.platform.ie) return enyo.platform.ie;
  200. else if (enyo.platform.ios) return enyo.platform.ios;
  201. else if (enyo.platform.webos) return enyo.platform.webos;
  202. else if (enyo.platform.blackberry) return enyo.platform.blackberry;
  203. else if (enyo.platform.safari) return enyo.platform.safari;
  204. else if (enyo.platform.chrome) return enyo.platform.chrome;
  205. else if (enyo.platform.firefox) return enyo.platform.firefox;
  206. return "?";
  207. }
  208. // Get client type
  209. util.getClientType = function() {
  210. return (document.location.protocol.substr(0,4) == "http" && !constant.noServerMode) ? constant.webAppType : constant.appType;
  211. }
  212. util.getClientName = function() {
  213. return this.getClientType() == constant.webAppType ? "Web App" : "App";
  214. }
  215. // URL location
  216. util.getCurrentServerUrl = function() {
  217. var url = window.location.href;
  218. return url.substring(0, url.lastIndexOf('/'));
  219. }
  220. // Min password size
  221. util.getMinPasswordSize = function() {
  222. var minSize = constant.minPasswordSize;
  223. var info = preferences.getServer();
  224. if (info && info.options && info.options["min-password-size"]) {
  225. minSize = info.options["min-password-size"];
  226. }
  227. return minSize;
  228. }
  229. // Restart application
  230. util.restartApp = function() {
  231. location.reload();
  232. }
  233. // Vibrate slightly the device
  234. util.vibrate = function() {
  235. if ((enyo.platform.android || enyo.platform.androidChrome || enyo.platform.ios) && util.getClientType() == constant.appType && navigator.vibrate) {
  236. navigator.vibrate(30);
  237. }
  238. }
  239. // Hide native toolbar
  240. util.hideNativeToolbar = function() {
  241. if ((enyo.platform.android || enyo.platform.androidChrome) && util.getClientType() == constant.appType && !constant.noServerMode) {
  242. AndroidFullScreen.immersiveMode(function() {}, function() {});
  243. }
  244. }
  245. // Handle volume buttons
  246. util.handleVolumeButtons = function() {
  247. if ((enyo.platform.android || enyo.platform.androidChrome) && util.getClientType() == constant.appType && !constant.noServerMode) {
  248. // HACK: Need only on Android because Cordova intercept volume buttons
  249. var emptyf = function() {};
  250. document.addEventListener("volumeupbutton", function() {
  251. cordova.plugins.VolumeControl.getVolume(function(value) {
  252. var volume = parseInt(value);
  253. if (volume < 100) {
  254. cordova.plugins.VolumeControl.setVolume((volume+10), emptyf, emptyf);
  255. }
  256. }, emptyf);
  257. }, false);
  258. document.addEventListener("volumedownbutton", function() {
  259. cordova.plugins.VolumeControl.getVolume(function(value) {
  260. var volume = parseInt(value);
  261. if (volume > 0) {
  262. cordova.plugins.VolumeControl.setVolume((volume-1), emptyf, emptyf);
  263. }
  264. }, emptyf);
  265. }, false);
  266. }
  267. }
  268. // Change browser fav icon and title
  269. var buddyIcon='<?xml version="1.0" encoding="UTF-8" standalone="no"?>\
  270. <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="16" height="16" version="1.0" >\
  271. <g transform="translate(37.943468,-309.4636)">\
  272. <g transform="matrix(0.05011994,0,0,0.05012004,-41.76673,299.66011)" style="fill:&fill_color;;fill-opacity:1;stroke:&stroke_color;;stroke-opacity:1">\
  273. <circle transform="matrix(0.969697,0,0,0.969697,-90.879133,125.06999)" style="fill:&fill_color;;fill-opacity:1;stroke:&stroke_color;;stroke-width:20.62502098;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" cx="331.38321" cy="134.2677" r="51.220825" />\
  274. <path d="m 290.55846,302.47333 -58.81513,59.20058 -59.39461,-59.40024 c -25.19828,-24.48771 -62.7038,13.33148 -38.1941,37.98719 l 60.04451,58.9817 -59.73639,59.42563 c -24.83976,24.97559 12.91592,63.26505 37.66786,37.75282 l 59.95799,-59.28294 58.75912,59.21065 c 24.50656,25.09065 62.43116,-13.00322 37.87956,-37.85772 l -59.24184,-59.02842 58.87574,-59.14782 c 25.1689,-25.18348 -13.0489,-62.75154 -37.80271,-37.84143 z" style="fill:&fill_color;;fill-opacity:1;stroke:&stroke_color;;stroke-width:20.00002098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />\
  275. </g></g></svg>';
  276. util.updateFavicon = function() {
  277. var color = preferences.getColor();
  278. var name = preferences.getName();
  279. if (!color) {
  280. color = {stroke: "#005FE4", fill: "#FF2B34"};
  281. }
  282. document.title = ((name&&name!="<No name>")?name+" - ":"")+"Sugarizer";
  283. var icon = buddyIcon.replace(new RegExp("&fill_color;","g"),color.fill).replace(new RegExp("&stroke_color;","g"),color.stroke);
  284. var svg_xml = (new XMLSerializer()).serializeToString((new DOMParser()).parseFromString(icon, "text/xml"));
  285. var canvas = document.createElement('canvas');
  286. canvas.width = 16;
  287. canvas.height = 16;
  288. var ctx = canvas.getContext('2d');
  289. var img = new Image();
  290. img.src = "data:image/svg+xml;base64," + btoa(svg_xml);
  291. img.onload = function() {
  292. ctx.drawImage(img, 0, 0);
  293. var link = document.querySelector("link[rel*='icon']") || document.createElement('link');
  294. link.type = 'image/x-icon';
  295. link.rel = 'shortcut icon';
  296. link.href = canvas.toDataURL("image/x-icon");
  297. document.getElementsByTagName('head')[0].appendChild(link);
  298. }
  299. }
  300. // Scan code functions
  301. var currentCamera = 0;
  302. var qrCancelCallback = null;
  303. function closeQR() {
  304. QRScanner.cancelScan(function(status){});
  305. if (qrCancelCallback) {
  306. qrCancelCallback();
  307. }
  308. document.getElementById("toolbar").style.opacity = 1;
  309. document.getElementById("canvas").style.opacity = 1;
  310. document.getElementById("qrclosebutton").style.visibility = "hidden";
  311. document.getElementById("qrswitchbutton").style.visibility = "hidden";
  312. var qrBackground = document.getElementById("qrbackground");
  313. qrBackground.parentNode.removeChild(qrBackground);
  314. }
  315. function switchCameraQR() {
  316. currentCamera = (currentCamera + 1) % 2;
  317. QRScanner.useCamera(currentCamera, function(err, status){});
  318. }
  319. util.scanQRCode = function(okCallback, cancelCallback) {
  320. currentCamera = 0;
  321. qrCancelCallback = cancelCallback;
  322. var qrBackground = document.createElement('div');
  323. qrBackground.className = "first-qrbackground";
  324. qrBackground.id = "qrbackground";
  325. document.getElementById("canvas").appendChild(qrBackground);
  326. document.getElementById("qrclosebutton").addEventListener('click', closeQR);
  327. document.getElementById("qrswitchbutton").addEventListener('click', switchCameraQR);
  328. QRScanner.prepare(function(err, status) {
  329. document.getElementById("toolbar").style.opacity = 0;
  330. document.getElementById("canvas").style.opacity = 0;
  331. document.getElementById("qrclosebutton").style.visibility = "visible";
  332. document.getElementById("qrswitchbutton").style.visibility = "visible";
  333. if (err) {
  334. console.log("Error "+err);
  335. } else {
  336. QRScanner.scan(function(err, code) {
  337. if (err) {
  338. console.log("Error "+err);
  339. } else {
  340. if (okCallback) {
  341. okCallback(code);
  342. }
  343. }
  344. util.cancelScan();
  345. document.getElementById("toolbar").style.opacity = 1;
  346. document.getElementById("canvas").style.opacity = 1;
  347. document.getElementById("qrclosebutton").style.visibility = "hidden";
  348. document.getElementById("qrswitchbutton").style.visibility = "hidden";
  349. });
  350. QRScanner.show(function(status) {});
  351. }
  352. });
  353. }
  354. util.cancelScan = function() {
  355. QRScanner.cancelScan(function(status){});
  356. qrCancelCallback = null;
  357. document.getElementById("qrclosebutton").removeEventListener('click', closeQR);
  358. document.getElementById("qrswitchbutton").removeEventListener('click', switchCameraQR);
  359. var qrBackground = document.getElementById("qrbackground");
  360. if (qrBackground) qrBackground.parentNode.removeChild(qrBackground);
  361. }
  362. // Decoding functions taken from
  363. // https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
  364. function b64ToUint6(nChr) {
  365. return nChr > 64 && nChr < 91 ?
  366. nChr - 65
  367. : nChr > 96 && nChr < 123 ?
  368. nChr - 71
  369. : nChr > 47 && nChr < 58 ?
  370. nChr + 4
  371. : nChr === 43 ?
  372. 62
  373. : nChr === 47 ?
  374. 63
  375. :
  376. 0;
  377. }
  378. function base64DecToArr(sBase64, nBlocksSize) {
  379. var
  380. sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length,
  381. nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen);
  382. for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
  383. nMod4 = nInIdx & 3;
  384. nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 6 * (3 - nMod4);
  385. if (nMod4 === 3 || nInLen - nInIdx === 1) {
  386. for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
  387. taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
  388. }
  389. nUint24 = 0;
  390. }
  391. }
  392. return taBytes;
  393. }
  394. // Write a new file
  395. util.writeFile = function(directory, metadata, content, callback) {
  396. var binary = null;
  397. var text = null;
  398. var extension = "json";
  399. var title = metadata.title;
  400. var mimetype = 'application/json';
  401. if (metadata && metadata.mimetype) {
  402. mimetype = metadata.mimetype;
  403. if (mimetype == "image/jpeg") {
  404. extension = "jpg";
  405. } else if (mimetype == "image/png") {
  406. extension = "png";
  407. } else if (mimetype == "audio/wav") {
  408. extension = "wav";
  409. } else if (mimetype == "video/webm") {
  410. extension = "webm";
  411. } else if (mimetype == "audio/mp3"||mimetype == "audio/mpeg") {
  412. extension = "mp3";
  413. } else if (mimetype == "video/mp4") {
  414. extension = "mp4";
  415. } else if (mimetype == "text/plain") {
  416. extension = "txt";
  417. text = JSON.parse(content);
  418. } else if (mimetype == "application/pdf") {
  419. extension = "pdf";
  420. } else if (mimetype == "application/msword") {
  421. extension = "doc";
  422. } else if (mimetype == "application/vnd.oasis.opendocument.text") {
  423. extension = "odt";
  424. } else {
  425. extension = "bin";
  426. }
  427. binary = base64DecToArr(content.substr(content.indexOf('base64,')+7)).buffer;
  428. } else {
  429. text = JSON.stringify({metadata: metadata, text: content});
  430. }
  431. var filename = title;
  432. if (filename.indexOf("."+extension)==-1) {
  433. filename += "."+extension;
  434. }
  435. if (util.getClientType() == constant.appType && (enyo.platform.android || enyo.platform.androidChrome || enyo.platform.ios || enyo.platform.electron) && !constant.noServerMode) {
  436. if (enyo.platform.android || enyo.platform.androidChrome || enyo.platform.ios) {
  437. var cordovaFileStorage = cordova.file.externalRootDirectory;
  438. if (enyo.platform.ios) {
  439. cordovaFileStorage = cordova.file.documentsDirectory;
  440. }
  441. window.resolveLocalFileSystemURL(cordovaFileStorage, function(directory) {
  442. directory.getFile(filename, {create:true}, function(file) {
  443. if (!file) {
  444. return;
  445. }
  446. file.createWriter(function(fileWriter) {
  447. fileWriter.seek(fileWriter.length);
  448. if (text) {
  449. var blob = new Blob([text], {type:mimetype});
  450. fileWriter.write(blob);
  451. } else {
  452. fileWriter.write(binary);
  453. }
  454. callback(null, file.fullPath);
  455. }, function(evt) {
  456. callback(error.code, null);
  457. });
  458. });
  459. });
  460. } else {
  461. var electron = require("electron");
  462. var ipc = electron.ipcRenderer;
  463. ipc.removeAllListeners('save-file-reply');
  464. ipc.send('save-file-dialog', {directory: directory, filename: filename, mimetype: mimetype, extension: extension, text: text, binary: content});
  465. ipc.on('save-file-reply', function(event, arg) {
  466. callback(arg.err, arg.filename);
  467. });
  468. }
  469. } else {
  470. var blob = new Blob((text?[text]:[binary]), {type:mimetype});
  471. FileSaver.saveAs(blob, filename);
  472. callback(null, filename);
  473. }
  474. }
  475. // Write file content to datastore
  476. function writeFileToStore(file, text, callback) {
  477. if (file.type == 'application/json') {
  478. // Handle JSON file
  479. var data = null;
  480. try {
  481. data = JSON.parse(text);
  482. if (!data.metadata) {
  483. callback(file.name, -1);
  484. return;
  485. }
  486. } catch(e) {
  487. callback(file.name, -1);
  488. return;
  489. }
  490. callback(file.name, 0, data.metadata, data.text);
  491. } else {
  492. var activity = "";
  493. if (file.type != "text/plain" && file.type != "application/pdf" && file.type != "application/msword" && file.type != "application/vnd.oasis.opendocument.text") {
  494. activity = "org.olpcfrance.MediaViewerActivity";
  495. }
  496. var metadata = {
  497. title: file.name,
  498. mimetype: file.type,
  499. activity: activity
  500. }
  501. callback(file.name, 0, metadata, text);
  502. }
  503. }
  504. // Load a file
  505. util.loadFile = function(file, callback) {
  506. // Use FileReaer object in web
  507. if (util.getClientType() == constant.webAppType || constant.noServerMode || (util.getClientType() == constant.appType && !enyo.platform.android && !enyo.platform.androidChrome && !enyo.platform.ios && !enyo.platform.electron)) {
  508. // Ensure type is valid
  509. var validTypes = ['application/json','image/jpeg','image/png','audio/wav','video/webm','audio/mp3','audio/mpeg','video/mp4','text/plain','application/pdf','application/msword','application/vnd.oasis.opendocument.text'];
  510. if (validTypes.indexOf(file.type) == -1) {
  511. callback(file.name, -1);
  512. return;
  513. }
  514. var reader = new FileReader();
  515. reader.onload = function() {
  516. writeFileToStore(file, reader.result, callback);
  517. };
  518. if (file.type == 'application/json') {
  519. reader.readAsText(file);
  520. } else {
  521. reader.readAsDataURL(file);
  522. }
  523. }
  524. }
  525. // Ask the user a directory (use to save a set of files)
  526. util.askDirectory = function(callback) {
  527. if (util.getClientType() != constant.appType || (enyo.platform.android || enyo.platform.androidChrome || enyo.platform.ios)) {
  528. return;
  529. }
  530. var electron = require("electron");
  531. var ipc = electron.ipcRenderer;
  532. ipc.removeAllListeners('choose-directory-reply');
  533. ipc.send('choose-directory-dialog');
  534. ipc.on('choose-directory-reply', function(event, arg) {
  535. callback(arg);
  536. })
  537. }
  538. // Ask the user a set files and write it to datastore
  539. util.askFiles = function(callback) {
  540. if (util.getClientType() != constant.appType) {
  541. return;
  542. }
  543. if (enyo.platform.android || enyo.platform.androidChrome || enyo.platform.ios) {
  544. if (!window.cordova && !window.PhoneGap) {
  545. return;
  546. }
  547. var file = {
  548. type: "image/jpeg",
  549. name: l10n.get("ImageFromDevice")
  550. };
  551. var captureSuccess = function(imageData) {
  552. var text = "data:image/jpeg;base64," + imageData;
  553. writeFileToStore(file, text, callback);
  554. }
  555. var captureError = function(error) {
  556. };
  557. navigator.camera.getPicture(captureSuccess, captureError, {
  558. quality: 50,
  559. targetWidth: 640,
  560. targetHeight: 480,
  561. destinationType: Camera.DestinationType.DATA_URL,
  562. sourceType: Camera.PictureSourceType.PHOTOLIBRARY
  563. });
  564. } else {
  565. var electron = require("electron");
  566. var ipc = electron.ipcRenderer;
  567. ipc.removeAllListeners('choose-files-reply');
  568. ipc.send('choose-files-dialog');
  569. ipc.on('choose-files-reply', function(event, file, err, text) {
  570. if (err) {
  571. callback(file.name, -1);
  572. } else {
  573. writeFileToStore(file, text, callback);
  574. }
  575. });
  576. }
  577. }
  578. function base64toBlob(mimetype, base64) {
  579. var contentType = mimetype;
  580. var byteCharacters = atob(base64.substr(base64.indexOf(';base64,')+8));
  581. var byteArrays = [];
  582. for (var offset = 0; offset < byteCharacters.length; offset += 1024) {
  583. var slice = byteCharacters.slice(offset, offset + 1024);
  584. var byteNumbers = new Array(slice.length);
  585. for (var i = 0; i < slice.length; i++) {
  586. byteNumbers[i] = slice.charCodeAt(i);
  587. }
  588. var byteArray = new Uint8Array(byteNumbers);
  589. byteArrays.push(byteArray);
  590. }
  591. var blob = new Blob(byteArrays, {type: contentType});
  592. return blob;
  593. }
  594. // Open the content as a document in a new Window
  595. util.openAsDocument = function(metadata, text) {
  596. if (util.getClientType() == constant.webAppType || (util.getClientType() == constant.appType && !enyo.platform.android && !enyo.platform.androidChrome && !enyo.platform.ios && !enyo.platform.electron) || constant.noServerMode) {
  597. // Convert blob object URL
  598. var blob = base64toBlob(metadata.mimetype, text);
  599. var blobUrl = URL.createObjectURL(blob);
  600. // Open in a new browser tab
  601. window.open(blobUrl, '_blank');
  602. } else if (enyo.platform.android || enyo.platform.androidChrome || enyo.platform.ios) {
  603. // Create a temporary file
  604. var cordovaFileStorage = cordova.file.externalCacheDirectory;
  605. if (enyo.platform.ios) {
  606. cordovaFileStorage = cordova.file.documentsDirectory;
  607. }
  608. window.resolveLocalFileSystemURL(cordovaFileStorage, function(directory) {
  609. directory.getFile("sugarizertemp", {create: true, exclusive: false}, function(file) {
  610. if (!file) {
  611. return;
  612. }
  613. file.createWriter(function(fileWriter) {
  614. fileWriter.seek(fileWriter.length);
  615. var blob = base64toBlob(metadata.mimetype, text)
  616. fileWriter.write(blob);
  617. // Open file in browser
  618. if (enyo.platform.ios) {
  619. // On iOS should be transfered in Cordova file space first
  620. var fileTransfer = new FileTransfer();
  621. var fileURL = "cdvfile://localhost/temporary/sugarizer/sugarizertemp";
  622. fileTransfer.download(
  623. cordovaFileStorage+"sugarizertemp",
  624. fileURL,
  625. function(entry) {
  626. cordova.InAppBrowser.open(fileURL, '_blank', 'location=no,closebuttoncaption='+l10n.get("Ok"));
  627. },
  628. function(error) {
  629. console.log("download error code " + error.code);
  630. },
  631. true
  632. );
  633. } else {
  634. // On Android, just open in the file system
  635. var filename = cordovaFileStorage+"sugarizertemp";
  636. cordova.InAppBrowser.open(filename, '_system');
  637. }
  638. }, function(evt) {
  639. });
  640. });
  641. });
  642. } else {
  643. // Save in a temporary file
  644. var electron = require("electron");
  645. var shell = electron.shell;
  646. var ipc = electron.ipcRenderer;
  647. ipc.removeAllListeners('create-tempfile-reply');
  648. ipc.send('create-tempfile', {metadata: metadata, text: text});
  649. ipc.on('create-tempfile-reply', function(event, file) {
  650. // Open in a shell
  651. shell.openExternal("file://"+file);
  652. });
  653. }
  654. }
  655. // Clean localStorage: datastore and settings
  656. util.cleanDatastore = function(full, then) {
  657. var results = datastore.find();
  658. var i = 0;
  659. var removeOneEntry = function(err) {
  660. if (++i < results.length) {
  661. datastore.remove(results[i].objectId, removeOneEntry);
  662. } else {
  663. if (then) {
  664. then();
  665. }
  666. }
  667. };
  668. preferences.reset(full);
  669. if (results.length) {
  670. datastore.remove(results[i].objectId, removeOneEntry);
  671. } else {
  672. if (then) {
  673. then();
  674. }
  675. }
  676. }
  677. // Compute storage size
  678. util.computeDatastoreSize = function(callback) {
  679. // Compute local storage size
  680. var used = 0;
  681. for(var x in localStorage) {
  682. var amount = (localStorage[x].length * 2);
  683. if (!isNaN(amount)) {
  684. used += amount;
  685. }
  686. }
  687. // Compute file size
  688. var results = datastore.find();
  689. for (var i = 0 ; i < results.length ; i++) {
  690. var entry = results[i];
  691. var textsize = entry.metadata["textsize"];
  692. if (textsize) {
  693. used += textsize;
  694. }
  695. }
  696. if (callback) {
  697. callback({bytes: used, formatted: util.formatSize(used)});
  698. }
  699. }
  700. // Format size
  701. util.formatSize = function(bytes) {
  702. var formatted = "";
  703. if (bytes > 1048576) {
  704. formated = (bytes/1024/1024).toFixed()+" "+l10n.get("ShortForMegabytes");
  705. } else if (bytes > 1024) {
  706. formated = (bytes/1024).toFixed()+" "+l10n.get("ShortForKilobytes");
  707. } else if (bytes == 0) {
  708. formated = "-";
  709. } else {
  710. formated = bytes + " " + l10n.get("ShortForBytes");
  711. }
  712. return formated;
  713. }
  714. // Quit application
  715. util.quitApp = function() {
  716. new Sugar.EE({mode: 2}).renderInto(document.getElementById("body"));
  717. window.setTimeout(function() {
  718. if (typeof chrome != 'undefined' && chrome.app && chrome.app.runtime) {
  719. window.top.postMessage("", '*');
  720. }
  721. window.close();
  722. if (!/Edge/.test(navigator.userAgent)) {
  723. window.open('', '_self', ''); // HACK: Not allowed on all browsers
  724. window.close();
  725. }
  726. if (navigator) {
  727. if (navigator.app)
  728. navigator.app.exitApp();
  729. else if (navigator.device)
  730. navigator.device.exitApp();
  731. }
  732. }, constant.timerBeforeClose);
  733. }
  734. return util;
  735. });