diff --git a/application/assets/images/call.svg b/application/assets/images/call.svg
new file mode 100644
index 0000000..e6e00d1
--- /dev/null
+++ b/application/assets/images/call.svg
@@ -0,0 +1,76 @@
+
+
diff --git a/application/assets/images/person.svg b/application/assets/images/person.svg
new file mode 100644
index 0000000..d3df7a1
--- /dev/null
+++ b/application/assets/images/person.svg
@@ -0,0 +1,132 @@
+
+
diff --git a/application/assets/js/helper.js b/application/assets/js/helper.js
index 2de285b..f8cd966 100644
--- a/application/assets/js/helper.js
+++ b/application/assets/js/helper.js
@@ -274,7 +274,6 @@ const helper = (() => {
}
let request = sdcard.get(filepath);
- console.log(filepath);
request.onsuccess = function (e) {
const file = e.target.result; // The retrieved file
@@ -284,7 +283,6 @@ const helper = (() => {
reader.addEventListener("load", function (event) {
callback(event.target.result);
- console.log(event.target.result);
});
reader.readAsText(file);
@@ -330,7 +328,7 @@ const helper = (() => {
// success copy and delete
let a = new_filename + "." + file_extension;
- callback(a);
+ callback(a, filename);
helper.side_toaster("successfully renamed", 3000);
};
diff --git a/application/assets/js/ical.min.js b/application/assets/js/ical.min.js
new file mode 100644
index 0000000..2f6656b
--- /dev/null
+++ b/application/assets/js/ical.min.js
@@ -0,0 +1,2 @@
+var ICAL;"object"==typeof module?ICAL=module.exports:"undefined"!=typeof HTMLScriptElement&&"noModule"in HTMLScriptElement.prototype?window.ICAL=ICAL={}:"object"!=typeof ICAL&&(ICAL={}),ICAL.foldLength=75,ICAL.newLineChar="\r\n",ICAL.helpers={updateTimezones:function(t){var e,n,r,i,s,a;if(!t||"vcalendar"!==t.name)return t;for(e=t.getAllSubcomponents(),n=[],r={},s=0;s>12&63,n=i>>6&63,r=63&i,h[o++]=s.charAt(i>>18&63)+s.charAt(e)+s.charAt(n)+s.charAt(r),a>16&255,n=s>>8&255,s=255&s,h[u++]=64==r?String.fromCharCode(e):64==i?String.fromCharCode(e,n):String.fromCharCode(e,n,s),o=this.changes.length)break}var a=this.changes[r];return a.utcOffset-a.prevUtcOffset<0&&0=this.changes.length?this.changes.length-1:t},_ensureCoverage:function(t){var e;-1==ICAL.Timezone._minimumExpansionYear&&(e=ICAL.Time.now(),ICAL.Timezone._minimumExpansionYear=e.year);var n=t;if(nICAL.Timezone.MAX_YEAR&&(n=ICAL.Timezone.MAX_YEAR),!this.changes.length||this.expandedUntilYeart)&&l);)u.year=l.year,u.month=l.month,u.day=l.day,u.hour=l.hour,u.minute=l.minute,u.second=l.second,u.isDate=l.isDate,ICAL.Timezone.adjust_change(u,0,0,0,-u.prevUtcOffset),n.push(u)}}else(u=s()).year=r.year,u.month=r.month,u.day=r.day,u.hour=r.hour,u.minute=r.minute,u.second=r.second,ICAL.Timezone.adjust_change(u,0,0,0,-u.prevUtcOffset),n.push(u);return n},toString:function(){return this.tznames||this.tzid}},ICAL.Timezone._compare_change_fn=function(t,e){return t.yeare.year?1:t.monthe.month?1:t.daye.day?1:t.houre.hour?1:t.minutee.minute?1:t.seconde.second?1:0},ICAL.Timezone.convert_time=function(t,e,n){if(t.isDate||e.tzid==n.tzid||e==ICAL.Timezone.localTimezone||n==ICAL.Timezone.localTimezone)return t.zone=n,t;e=e.utcOffset(t);return t.adjust(0,0,0,-e),e=n.utcOffset(t),t.adjust(0,0,0,e),null},ICAL.Timezone.fromData=function(t){return(new ICAL.Timezone).fromData(t)},ICAL.Timezone.utcTimezone=ICAL.Timezone.fromData({tzid:"UTC"}),ICAL.Timezone.localTimezone=ICAL.Timezone.fromData({tzid:"floating"}),ICAL.Timezone.adjust_change=function(t,e,n,r,i){return ICAL.Time.prototype.adjust.call(t,e,n,r,i,t)},ICAL.Timezone._minimumExpansionYear=-1,ICAL.Timezone.MAX_YEAR=2035,ICAL.Timezone.EXTRA_COVERAGE=5}(),ICAL.TimezoneService=function(){var n,t={get count(){return Object.keys(n).length},reset:function(){n=Object.create(null);var t=ICAL.Timezone.utcTimezone;n.Z=t,n.UTC=t,n.GMT=t},has:function(t){return!!n[t]},get:function(t){return n[t]},register:function(t,e){if(t instanceof ICAL.Component&&"vtimezone"===t.name&&(t=(e=new ICAL.Timezone(t)).tzid),!(e instanceof ICAL.Timezone))throw new TypeError("timezone must be ICAL.Timezone or ICAL.Component");n[t]=e},remove:function(t){return delete n[t]}};return t.reset(),t}(),function(){function t(e){Object.defineProperty(ICAL.Time.prototype,e,{get:function(){return this._pendingNormalization&&(this._normalize(),this._pendingNormalization=!1),this._time[e]},set:function(t){return"isDate"===e&&t&&!this._time.isDate&&this.adjust(0,0,0,0),this._cachedUnixTime=null,this._pendingNormalization=!0,this._time[e]=t}})}ICAL.Time=function(t,e){var n=(this.wrappedJSObject=this)._time=Object.create(null);n.year=0,n.month=1,n.day=1,n.hour=0,n.minute=0,n.second=0,n.isDate=!1,this.fromData(t,e)},ICAL.Time._dowCache={},ICAL.Time._wnCache={},ICAL.Time.prototype={icalclass:"icaltime",_cachedUnixTime:null,get icaltype(){return this.isDate?"date":"date-time"},zone:null,_pendingNormalization:!1,clone:function(){return new ICAL.Time(this._time,this.zone)},reset:function(){this.fromData(ICAL.Time.epochTime),this.zone=ICAL.Timezone.utcTimezone},resetTo:function(t,e,n,r,i,s,a){this.fromData({year:t,month:e,day:n,hour:r,minute:i,second:s,zone:a})},fromJSDate:function(t,e){return t?e?(this.zone=ICAL.Timezone.utcTimezone,this.year=t.getUTCFullYear(),this.month=t.getUTCMonth()+1,this.day=t.getUTCDate(),this.hour=t.getUTCHours(),this.minute=t.getUTCMinutes(),this.second=t.getUTCSeconds()):(this.zone=ICAL.Timezone.localTimezone,this.year=t.getFullYear(),this.month=t.getMonth()+1,this.day=t.getDate(),this.hour=t.getHours(),this.minute=t.getMinutes(),this.second=t.getSeconds()):this.reset(),this._cachedUnixTime=null,this},fromData:function(t,e){if(t)for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&"icaltype"!==n&&(this[n]=t[n]);return e&&(this.zone=e),!t||"isDate"in t?t&&"isDate"in t&&(this.isDate=t.isDate):this.isDate=!("hour"in t),t&&"timezone"in t&&(e=ICAL.TimezoneService.get(t.timezone),this.zone=e||ICAL.Timezone.localTimezone),t&&"zone"in t&&(this.zone=t.zone),this.zone||(this.zone=ICAL.Timezone.localTimezone),this._cachedUnixTime=null,this},dayOfWeek:function(t){var e=t||ICAL.Time.SUNDAY,n=(this.year<<12)+(this.month<<8)+(this.day<<3)+e;if(n in ICAL.Time._dowCache)return ICAL.Time._dowCache[n];var r=this.day,i=this.month+(this.month<3?12:0),t=this.year-(this.month<3?1:0),i=r+t+ICAL.helpers.trunc(26*(i+1)/10)+ICAL.helpers.trunc(t/4);return i+=6*ICAL.helpers.trunc(t/100)+ICAL.helpers.trunc(t/400),ICAL.Time._dowCache[n]=i=(i+7-e)%7+1},dayOfYear:function(){var t=ICAL.Time.isLeapYear(this.year)?1:0;return ICAL.Time.daysInYearPassedMonth[t][this.month-1]+this.day},startOfWeek:function(t){var e=t||ICAL.Time.SUNDAY,t=this.clone();return t.day-=(this.dayOfWeek()+7-e)%7,t.isDate=!0,t.hour=0,t.minute=0,t.second=0,t},endOfWeek:function(t){var e=t||ICAL.Time.SUNDAY,t=this.clone();return t.day+=(7-this.dayOfWeek()+e-ICAL.Time.SUNDAY)%7,t.isDate=!0,t.hour=0,t.minute=0,t.second=0,t},startOfMonth:function(){var t=this.clone();return t.day=1,t.isDate=!0,t.hour=0,t.minute=0,t.second=0,t},endOfMonth:function(){var t=this.clone();return t.day=ICAL.Time.daysInMonth(t.month,t.year),t.isDate=!0,t.hour=0,t.minute=0,t.second=0,t},startOfYear:function(){var t=this.clone();return t.day=1,t.month=1,t.isDate=!0,t.hour=0,t.minute=0,t.second=0,t},endOfYear:function(){var t=this.clone();return t.day=31,t.month=12,t.isDate=!0,t.hour=0,t.minute=0,t.second=0,t},startDoyWeek:function(t){t=t||ICAL.Time.SUNDAY,t=this.dayOfWeek()-t;return t<0&&(t+=7),this.dayOfYear()-t},getDominicalLetter:function(){return ICAL.Time.getDominicalLetter(this.year)},nthWeekDay:function(t,e){var n=ICAL.Time.daysInMonth(this.month,this.year),r=e,i=0,s=this.clone(),a=0<=r?(s.day=1,0!=r&&r--,i=s.day,(e=t-s.dayOfWeek())<0&&(e+=7),i+=e,i-=t,t):(s.day=n,r++,(a=s.dayOfWeek()-t)<0&&(a+=7),n-a);return i+(a+=7*r)},isNthWeekDay:function(t,e){var n=this.dayOfWeek();return 0===e&&n===t||this.nthWeekDay(t,e)===this.day},weekNumber:function(t){var e=(this.year<<12)+(this.month<<8)+(this.day<<3)+t;if(e in ICAL.Time._wnCache)return ICAL.Time._wnCache[e];var n=this.clone();n.isDate=!0;var r=this.year;12==n.month&&25ICAL.Time.daysInYearPassedMonth[i][12])return i=ICAL.Time.isLeapYear(e)?1:0,n-=ICAL.Time.daysInYearPassedMonth[i][12],ICAL.Time.fromDayOfYear(n,++e);r.year=e,r.isDate=!0;for(var s=11;0<=s;s--)if(n>ICAL.Time.daysInYearPassedMonth[i][s]){r.month=s+1,r.day=n-ICAL.Time.daysInYearPassedMonth[i][s];break}return r.auto_normalize=!0,r},ICAL.Time.fromStringv2=function(t){return new ICAL.Time({year:parseInt(t.substr(0,4),10),month:parseInt(t.substr(5,2),10),day:parseInt(t.substr(8,2),10),isDate:!0})},ICAL.Time.fromDateString=function(t){return new ICAL.Time({year:ICAL.helpers.strictParseInt(t.substr(0,4)),month:ICAL.helpers.strictParseInt(t.substr(5,2)),day:ICAL.helpers.strictParseInt(t.substr(8,2)),isDate:!0})},ICAL.Time.fromDateTimeString=function(t,e){if(t.length<19)throw new Error('invalid date-time value: "'+t+'"');var n;return t[19]&&"Z"===t[19]?n="Z":e&&(n=e.getParameter("tzid")),new ICAL.Time({year:ICAL.helpers.strictParseInt(t.substr(0,4)),month:ICAL.helpers.strictParseInt(t.substr(5,2)),day:ICAL.helpers.strictParseInt(t.substr(8,2)),hour:ICAL.helpers.strictParseInt(t.substr(11,2)),minute:ICAL.helpers.strictParseInt(t.substr(14,2)),second:ICAL.helpers.strictParseInt(t.substr(17,2)),timezone:n})},ICAL.Time.fromString=function(t,e){return 10ICAL.Time.THURSDAY&&(n.day+=7),e>ICAL.Time.THURSDAY&&(n.day-=7),n.day-=t-e,n},ICAL.Time.getDominicalLetter=function(t){var e="GFEDCBA",n=(t+(t/4|0)+(t/400|0)-(t/100|0)-1)%7;return ICAL.Time.isLeapYear(t)?e[(6+n)%7]+e[n]:e[n]},ICAL.Time.epochTime=ICAL.Time.fromData({year:1970,month:1,day:1,hour:0,minute:0,second:0,isDate:!1,timezone:"Z"}),ICAL.Time._cmp_attr=function(t,e,n){return t[n]>e[n]?1:t[n] '+e);if(void 0!==n&&ns||0==this.last.day)throw new Error("Malformed values in BYDAY part")}else this.has_by_data("BYMONTHDAY")&&this.last.day<0&&(s=ICAL.Time.daysInMonth(this.last.month,this.last.year),this.last.day=s+this.last.day+1)},next:function(){var t,e=this.last?this.last.clone():null;if(this.rule.count&&this.occurrence_number>=this.rule.count||this.rule.until&&0i)){if(r<0)r=i+(r+1);else if(0===r)continue;-1===s.indexOf(r)&&s.push(r)}return s.sort(function(t,e){return t-e})},_byDayAndMonthDay:function(t){var e,n,r,i,s=this.by_data.BYDAY,a=0,o=s.length,u=0,h=this,c=this.last.day;function l(){for(i=ICAL.Time.daysInMonth(h.last.month,h.last.year),e=h.normalizeByMonthDayRules(h.last.year,h.last.month,h.by_data.BYMONTHDAY),r=e.length;e[a]<=c&&(!t||e[a]!=c)&&a=this.by_data.BYMONTHDAY.length&&(this.by_indices.BYMONTHDAY=0,this.increment_month()),(e=ICAL.Time.daysInMonth(this.last.month,this.last.year))<(s=(s=this.by_data.BYMONTHDAY[this.by_indices.BYMONTHDAY])<0?e+s+1:s)?(this.last.day=1,t=this.is_day_in_byday(this.last)):this.last.day=s):(this.increment_month(),e=ICAL.Time.daysInMonth(this.last.month,this.last.year),this.by_data.BYMONTHDAY[0]>e?t=0:this.last.day=this.by_data.BYMONTHDAY[0]);return t},next_weekday_by_week:function(){var t=0;if(0==this.next_hour())return t;if(!this.has_by_data("BYDAY"))return 1;for(;;){var e=new ICAL.Time;this.by_indices.BYDAY++,this.by_indices.BYDAY==Object.keys(this.by_data.BYDAY).length&&(this.by_indices.BYDAY=0,t=1);var n=this.by_data.BYDAY[this.by_indices.BYDAY],n=this.ruleDayOfWeek(n)[1];(n-=this.rule.wkst)<0&&(n+=7),e.year=this.last.year,e.month=this.last.month,e.day=this.last.day;e=e.startDoyWeek(this.rule.wkst);if(!(n+e<1)||t){n=ICAL.Time.fromDayOfYear(e+n,this.last.year);return this.last.year=n.year,this.last.month=n.month,this.last.day=n.day,t}}},next_year:function(){if(0==this.next_hour())return 0;if(++this.days_index==this.days.length)for(this.days_index=0;this.increment_year(this.rule.interval),this.expand_year_days(this.last.year),0==this.days.length;);return this._nextByYearDay(),1},_nextByYearDay:function(){var t=this.days[this.days_index],e=this.last.year;t<1&&(t+=1,e+=1);e=ICAL.Time.fromDayOfYear(t,e);this.last.day=e.day,this.last.month=e.month},ruleDayOfWeek:function(t,e){var n=t.match(/([+-]?[0-9])?(MO|TU|WE|TH|FR|SA|SU)/);return n?[parseInt(n[1]||0,10),t=ICAL.Recur.icalDayToNumericDay(n[2],e)]:[0,0]},next_generic:function(t,e,n,r,i){var s=t in this.by_data,a=this.rule.freq==e,e=0;return i&&0==this[i]()||(s?(this.by_indices[t]++,this.by_indices[t],i=this.by_data[t],this.by_indices[t]==i.length&&(this.by_indices[t]=0,e=1),this.last[n]=i[this.by_indices[t]]):a&&this["increment_"+n](this.rule.interval),s&&e&&a&&this["increment_"+r](1)),e},increment_monthday:function(t){for(var e=0;en&&(this.last.day-=n,this.increment_month())}},increment_month:function(){var t;this.last.day=1,this.has_by_data("BYMONTH")?(this.by_indices.BYMONTH++,this.by_indices.BYMONTH==this.by_data.BYMONTH.length&&(this.by_indices.BYMONTH=0,this.increment_year(1)),this.last.month=this.by_data.BYMONTH[this.by_indices.BYMONTH]):("MONTHLY"==this.rule.freq?this.last.month+=this.rule.interval:this.last.month++,this.last.month--,t=ICAL.helpers.trunc(this.last.month/12),this.last.month%=12,this.last.month++,0!=t&&this.increment_year(t))},increment_year:function(t){this.last.year+=t},increment_generic:function(t,e,n,r){this.last[e]+=t;t=ICAL.helpers.trunc(this.last[e]/n);this.last[e]%=n,0!=t&&this["increment_"+r](t)},has_by_data:function(t){return t in this.rule.parts},expand_year_days:function(t){var e=new ICAL.Time;this.days=[];var n,r,i={},s=["BYDAY","BYWEEKNO","BYMONTHDAY","BYMONTH","BYYEARDAY"];for(n in s)!s.hasOwnProperty(n)||(r=s[n])in this.rule.parts&&(i[r]=this.rule.parts[r]);if("BYMONTH"in i&&"BYWEEKNO"in i){var a=1,o={};e.year=t,e.isDate=!0;for(var u=0;ue[0]?1:e[0]>t[0]?-1:0}return t.prototype={THISANDFUTURE:"THISANDFUTURE",exceptions:null,strictExceptions:!1,relateException:function(t){if(this.isRecurrenceException())throw new Error("cannot relate exception to exceptions");if(t instanceof ICAL.Component&&(t=new ICAL.Event(t)),this.strictExceptions&&t.uid!==this.uid)throw new Error("attempted to relate unrelated exception");var e=t.recurrenceId.toString();(this.exceptions[e]=t).modifiesFuture()&&(t=[t.recurrenceId.toUnixTime(),e],e=ICAL.helpers.binsearchInsert(this.rangeExceptions,t,n),this.rangeExceptions.splice(e,0,t))},modifiesFuture:function(){return!!this.component.hasProperty("recurrence-id")&&this.component.getFirstProperty("recurrence-id").getParameter("range")===this.THISANDFUTURE},findRangeException:function(t){if(!this.rangeExceptions.length)return null;var e=t.toUnixTime(),t=ICAL.helpers.binsearchInsert(this.rangeExceptions,[e],n);if(--t<0)return null;t=this.rangeExceptions[t];return e {
}
};
+ let pickContact = (callback) => {
+ try {
+ let pick = new MozActivity({
+ name: "pick",
+ data: {
+ type: ["webcontacts/contact"],
+ },
+ });
+
+ pick.onsuccess = function (e) {
+ callback(pick.result.contact, "k2");
+ };
+
+ pick.onerror = function () {
+ general.blocker = false;
+ console.log("The activity encounter en error: " + this.error);
+ };
+ } catch (e) {
+ console.log(e);
+ general.blocker = false;
+ }
+
+ if ("b2g" in navigator) {
+ let pick = new WebActivity("pick", {
+ type: "webcontacts/contact",
+ });
+
+ pick.start().then(
+ (rv) => {
+ general.blocker = false;
+
+ callback(rv.contact, "k3");
+ },
+ (err) => {
+ general.blocker = false;
+
+ console.log(err);
+ }
+ );
+ }
+ };
+
+ var sms = (n) => {
+ const smsLink = document.createElement("a");
+
+ smsLink.href = "sms:" + n;
+
+ smsLink.textContent = "Call Now";
+
+ document.body.appendChild(smsLink);
+
+ smsLink.addEventListener("click", function () {});
+
+ smsLink.click();
+ document.body.removeChild(smsLink);
+ };
+
+ let dial = (n) => {
+ // Create the element
+ const telLink = document.createElement("a");
+
+ // Set the href attribute with the tel: protocol
+ telLink.href = "tel:" + n;
+
+ // Set the text content of the link
+ telLink.textContent = "Call Now";
+
+ // Append the link to the body (or any other desired container)
+ document.body.appendChild(telLink);
+
+ telLink.addEventListener("click", function () {});
+
+ telLink.click();
+
+ document.body.removeChild(telLink);
+ };
+
return {
photo,
openSettings,
pickGallery,
+ dial,
+ sms,
+ pickContact,
};
})();
diff --git a/application/assets/js/vcard-parser.js b/application/assets/js/vcard-parser.js
deleted file mode 100644
index b262879..0000000
--- a/application/assets/js/vcard-parser.js
+++ /dev/null
@@ -1,230 +0,0 @@
-var vCardParser = (function () {
- var fieldPropertyMapping = {
- "TITLE": "title",
- "TEL": "telephone",
- "FN": "displayName",
- "N": "name",
- "EMAIL": "email",
- "CATEGORIES": "categories",
- "ADR": "address",
- "URL": "url",
- "NOTE": "notes",
- "ORG": "organization",
- "BDAY": "birthday",
- "PHOTO": "photo",
- };
-
- function lookupField(context, fieldName) {
- var propertyName = fieldPropertyMapping[fieldName];
-
- if (!propertyName && fieldName !== "BEGIN" && fieldName !== "END") {
- context.info("define property name for " + fieldName);
- propertyName = fieldName;
- }
-
- return propertyName;
- }
-
- function removeWeirdItemPrefix(line) {
- // sometimes lines are prefixed by "item" keyword like "item1.ADR;type=WORK:....."
- return line.substring(0, 4) === "item"
- ? line.match(/item\d\.(.*)/)[1]
- : line;
- }
-
- function singleLine(context, fieldValue, fieldName) {
- // convert escaped new lines to real new lines.
- fieldValue = fieldValue.replace("\\n", "\n");
-
- // append value if previously specified
- if (context.currentCard[fieldName]) {
- context.currentCard[fieldName] += "\n" + fieldValue;
- } else {
- context.currentCard[fieldName] = fieldValue;
- }
- }
-
- function typedLine(context, fieldValue, fieldName, typeInfo, valueFormatter) {
- var isDefault = false;
-
- // strip type info and find out is that preferred value
- typeInfo = typeInfo.filter(function (type) {
- isDefault = isDefault || type.name === "PREF";
- return type.name !== "PREF";
- });
-
- typeInfo = typeInfo.reduce(function (p, c) {
- p[c.name] = c.value;
- return p;
- }, {});
-
- context.currentCard[fieldName] = context.currentCard[fieldName] || [];
-
- context.currentCard[fieldName].push({
- isDefault: isDefault,
- valueInfo: typeInfo,
- value: valueFormatter ? valueFormatter(fieldValue) : fieldValue,
- });
- }
-
- function commaSeparatedLine(context, fieldValue, fieldName) {
- context.currentCard[fieldName] = fieldValue.split(",");
- }
-
- function dateLine(context, fieldValue, fieldName) {
- // if value is in "19531015T231000Z" format strip time field and use date value.
- fieldValue =
- fieldValue.length === 16 ? fieldValue.substr(0, 8) : fieldValue;
-
- var dateValue;
-
- if (fieldValue.length === 8) {
- // "19960415" format ?
- dateValue = new Date(
- fieldValue.substr(0, 4),
- fieldValue.substr(4, 2),
- fieldValue.substr(6, 2)
- );
- } else {
- // last chance to try as date.
- dateValue = new Date(fieldValue);
- }
-
- if (!dateValue || isNaN(dateValue.getDate())) {
- dateValue = null;
- context.error("invalid date format " + fieldValue);
- }
-
- context.currentCard[fieldName] = dateValue && dateValue.toJSON(); // always return the ISO date format
- }
-
- function structured(fields) {
- return function (context, fieldValue, fieldName) {
- var values = fieldValue.split(";");
-
- context.currentCard[fieldName] = fields.reduce(function (p, c, i) {
- p[c] = values[i] || "";
- return p;
- }, {});
- };
- }
-
- function addressLine(context, fieldValue, fieldName, typeInfo) {
- typedLine(context, fieldValue, fieldName, typeInfo, function (value) {
- var names = value.split(";");
-
- return {
- // ADR field sequence
- postOfficeBox: names[0],
- number: names[1],
- street: names[2] || "",
- city: names[3] || "",
- region: names[4] || "",
- postalCode: names[5] || "",
- country: names[6] || "",
- };
- });
- }
-
- function noop() {}
-
- function endCard(context) {
- // store card in context and create a new card.
- context.cards.push(context.currentCard);
- context.currentCard = {};
- }
-
- var fieldParsers = {
- "BEGIN": noop,
- "VERSION": noop,
- "N": structured(["surname", "name", "additionalName", "prefix", "suffix"]),
- "TITLE": singleLine,
- "TEL": typedLine,
- "EMAIL": typedLine,
- "ADR": addressLine,
- "NOTE": singleLine,
- "NICKNAME": commaSeparatedLine,
- "BDAY": dateLine,
- "URL": singleLine,
- "CATEGORIES": commaSeparatedLine,
- "END": endCard,
- "FN": singleLine,
- "ORG": singleLine,
- "UID": singleLine,
- "PHOTO": singleLine,
- };
-
- function feedData(context) {
- for (var i = 0; i < context.data.length; i++) {
- var line = removeWeirdItemPrefix(context.data[i]);
-
- var pairs = line.split(":"),
- fieldName = pairs[0],
- fieldTypeInfo,
- fieldValue = pairs.slice(1).join(":");
-
- // is additional type info provided ?
- if (
- fieldName.indexOf(";") >= 0 &&
- line.indexOf(";") < line.indexOf(":")
- ) {
- var typeInfo = fieldName.split(";");
- fieldName = typeInfo[0];
- fieldTypeInfo = typeInfo.slice(1).map(function (type) {
- var info = type.split("=");
- return {
- name: info[0].toLowerCase(),
- value: info[1].replace(/"(.*)"/, "$1"),
- };
- });
- }
-
- // ensure fieldType is in upper case
- fieldName = fieldName.toUpperCase();
-
- var fieldHandler = fieldParsers[fieldName];
-
- if (fieldHandler) {
- fieldHandler(
- context,
- fieldValue,
- lookupField(context, fieldName),
- fieldTypeInfo
- );
- } else if (fieldName.substring(0, 2) != "X-") {
- // ignore X- prefixed extension fields.
- context.info(
- "unknown field " + fieldName + " with value " + fieldValue
- );
- }
- }
- }
-
- function parse(data) {
- var lines = data
- // replace escaped new lines
- .replace(/\n\s{1}/g, "")
- // split if a character is directly after a newline
- .split(/\r\n(?=\S)|\r(?=\S)|\n(?=\S)/);
-
- var context = {
- info: function (desc) {
- console.info(desc);
- },
- error: function (err) {
- console.error(err);
- },
- data: lines,
- currentCard: {},
- cards: [],
- };
-
- feedData(context);
-
- return context.cards;
- }
-
- return {
- parse: parse,
- };
-})();
diff --git a/application/index.html b/application/index.html
index df4306d..8370c29 100644
--- a/application/index.html
+++ b/application/index.html
@@ -54,7 +54,11 @@
-
+
+
+
+
+
diff --git a/application/index.js b/application/index.js
index dba1ef0..471c5e8 100644
--- a/application/index.js
+++ b/application/index.js
@@ -4,21 +4,23 @@ let debug = false;
let filter_query;
let file_content = [];
let content = "";
+let vcard = false;
let current_file;
let files = JSON.parse(localStorage.getItem("files")) || [];
-console.log("start:", files);
let action = null;
let action_element = null;
let selected_image;
let selected_image_url;
let qrcode_content;
-let status;
+
+let vcard_content = { tel: "", email: "" };
let general = {
fileAction: false,
importAction: false,
blocker: false,
+ returned_from_scanning: false,
};
if (debug) {
@@ -43,17 +45,108 @@ let url_test = (string) => {
//https://github.com/ertant/vCard
-let vcard_parser = (string) => {
- console.log(vCardParser.parse(string));
+let vcard_parser = (vCardData, callback) => {
+ // Default values
+ const defaultValues = {
+ fn: "Unknown",
+ org: "Unknown",
+ tel: "Not available",
+ email: "Not available",
+ adr: "Not available",
+ bday: "Not available",
+ note: "No notes",
+ };
+
+ let vCardComponent;
+
+ try {
+ // Convert vCard data to jCal format (required by ical.js)
+ const jcalData = ICAL.parse(vCardData);
+
+ // Create a component from jCal data
+ vCardComponent = new ICAL.Component(jcalData);
+ } catch (error) {
+ console.error("Error parsing vCard:", error.message);
+ // Handle the error or log it as needed
+ vcard_content = { tel: "", email: "" };
+ }
+
+ // Continue with parsing and accessing properties if vCardComponent is defined
+ if (vCardComponent) {
+ // Access vCard properties
+
+ vcard_content.email = getValue("email", vCardComponent);
+ vcard_content.tel = getValue("tel", vCardComponent);
+
+ callback();
+ }
+
+ function getValue(propertyName, component) {
+ try {
+ const propertyValue = component.getFirstPropertyValue(propertyName);
+ return propertyValue || "";
+ } catch (error) {
+ console.error(`Error accessing ${propertyName}: ${error.message}`);
+ return defaultValues[propertyName] || "Not available";
+ }
+ }
};
function formatVCardContent(content) {
// Replace LF with CRLF and add CRLF at the end if not present
- return (
- content.replace(/\r?\n/g, "\r\n") + (content.endsWith("\r\n") ? "" : "\r\n")
- );
+ const formattedContent =
+ content.replace(/\r?\n/g, "\r\n") +
+ (content.endsWith("\r\n") ? "" : "\r\n");
+
+ // Split the content into lines
+ const lines = formattedContent.split(/\r?\n/);
+
+ // Filter out lines that represent empty values
+ const nonEmptyLines = lines.filter((line) => !isEmptyValue(line));
+
+ // Join the non-empty lines back into a formatted vCard content
+ const result = nonEmptyLines.join("\r\n");
+
+ return result;
+}
+
+function isEmptyValue(line) {
+ // Check if a line represents an empty value (e.g., "TEL:")
+ return /^[\w-]+:$/i.test(line.trim());
}
+const create_vcard = (data) => {
+ try {
+ // Create a new vCard component from a string
+ const newVCard = ICAL.Component.fromString(
+ "BEGIN:VCARD\nVERSION:3.0\nEND:VCARD"
+ );
+
+ // Reset blocker flag
+
+ // Add properties based on data
+ if (data.fullName !== "")
+ newVCard.addPropertyWithValue("FN", data.fullName);
+ if (data.tel !== "") newVCard.addPropertyWithValue("TEL", data.tel);
+ if (data.email !== "") newVCard.addPropertyWithValue("EMAIL", data.email);
+
+ // Get the string representation of the new vCard
+ const newVCardString = newVCard.toString();
+
+ // Assign the vCard string to some variable (assuming 'qrcode_content' is a global variable)
+ qrcode_content = newVCardString;
+ general.blocker = false;
+
+ console.log("vCard creation successful");
+ } catch (error) {
+ // Handle errors
+ console.error("Error during vCard creation:", error);
+ // Set blocker flag or perform other error handling if needed
+ side_toaster("Error during vCard creation", 3000);
+ general.blocker = false;
+ }
+};
+
let set_tabindex = () => {
document
.querySelectorAll('.item:not([style*="display: none"]')
@@ -142,7 +235,6 @@ try {
let filename_list = [];
//list files
let read_files = (callback) => {
- //files = [];
try {
var d = navigator.getDeviceStorage("sdcard");
@@ -152,11 +244,8 @@ let read_files = (callback) => {
if (!this.result) {
// Remove element from files array
- for (let i = 0; i < files.length; i++) {
- if (filename_list.indexOf(files[i].path) == -1) {
- files.splice(i, 1);
- }
- }
+ files = files.filter((file) => filename_list.includes(file.path));
+
save_files();
m.route.set("/start");
@@ -177,10 +266,15 @@ let read_files = (callback) => {
//update files array if exist
filename_list.push(file.name);
let fileExists = false;
- files.forEach((e) => {
+ files.forEach((e, i) => {
if (e.path == file.name) {
fileExists = true;
e.file = f;
+
+ if (file.lastModified != e.modified) {
+ fileExists = false;
+ files.splice(i, 1);
+ }
}
});
@@ -191,6 +285,7 @@ let read_files = (callback) => {
"file": f,
"type": type[type.length - 1],
"qr": true,
+ "modified": file.lastModified,
});
}
}
@@ -216,8 +311,8 @@ let read_files = (callback) => {
if (!file.done) {
let m = file.value.name.split("/");
let file_name = m[m.length - 1];
-
let f = "";
+ let type = file_name.split(".");
try {
f = URL.createObjectURL(file.value);
@@ -227,28 +322,41 @@ let read_files = (callback) => {
file.value.name.includes("/passport/") &&
!file.value.name.includes("/sdcard/.")
) {
+ filename_list.push(file.value.name);
+
let fileExists = false;
- files.forEach((e) => {
- if (e.path == file.name) {
+ files.forEach((e, i) => {
+ if (e.path == file.value.name) {
fileExists = true;
e.file = f;
+
+ if (file.value.lastModified != e.modified) {
+ fileExists = false;
+ files.splice(i, 1);
+ }
}
});
if (!fileExists) {
files.push({
- "path": file.name,
+ "path": file.value.name,
"name": file_name,
"file": f,
"type": type[type.length - 1],
"qr": true,
+ "modified": file.value.lastModified,
});
}
}
next(_files);
}
- if (file.done) m.route.set("/start");
+ if (file.done) {
+ files = files.filter((file) => filename_list.includes(file.path));
+
+ save_files();
+ m.route.set("/start");
+ }
})
.catch(() => {
next(_files);
@@ -409,7 +517,6 @@ function write_file(data, filename, filetype) {
var request = sdcard.addNamed(file, filename);
request.onsuccess = function () {
- //files = [];
read_files();
startup = false;
m.route.set("/start?focus=" + filename);
@@ -423,7 +530,7 @@ function write_file(data, filename, filetype) {
}
let generate_qr = (string) => {
- status = "";
+ general.returned_from_scanning = false;
var qrs = new QRious();
@@ -476,7 +583,7 @@ document.addEventListener("DOMContentLoaded", function () {
} else {
document.querySelector("#intro").style.display = "none";
t = 0;
- status = "";
+ general.returned_from_scanning = false;
}
},
},
@@ -486,7 +593,9 @@ document.addEventListener("DOMContentLoaded", function () {
"ul",
{
id: "files-list",
- oninit: () => {},
+ oninit: () => {
+ vcard_content = {};
+ },
oncreate: ({ dom }) => {
setTimeout(() => {
if (files.length == 0) {
@@ -569,11 +678,11 @@ document.addEventListener("DOMContentLoaded", function () {
m.trust(
"" +
"Passport
" +
- "The app is a file viewer for JPG, PNG, and PDF files. It should help you display your QR code tickets more quickly during checks. The files must be stored in the directory /passport so that they can be displayed.
" +
+ "The app is a file viewer for JPG, PNG,vCard and PDF files. It should help you display your QR code tickets more quickly during checks. The files must be stored in the directory /passport so that they can be displayed.
" +
"" +
"" +
"Good to know
With key 2, you can rename or delete files
" +
- "Credits
Mithril.js, PDF.js
" +
+ "Credits
Mithril.js, PDF.js, ical.js
" +
"" +
"License
MIT
" +
"" +
@@ -639,9 +748,21 @@ document.addEventListener("DOMContentLoaded", function () {
},
};
- let read_file_callback = (e) => {
+ let read_vcard_file_callback = (e) => {
content = e;
document.querySelector("#qr-content").textContent = e;
+
+ vcard_parser(e, () => {
+ if (vcard_content.tel != "") {
+ helper.bottom_bar(
+ "",
+ "",
+ ""
+ );
+ } else {
+ helper.bottom_bar("", "", "");
+ }
+ });
};
var show_vcf = {
view: function () {
@@ -652,7 +773,8 @@ document.addEventListener("DOMContentLoaded", function () {
id: "qr-content",
oninit: () => {
helper.bottom_bar("", "", "");
- helper.readFile(selected_image_url, read_file_callback);
+
+ helper.readFile(selected_image_url, read_vcard_file_callback);
},
},
""
@@ -667,28 +789,41 @@ document.addEventListener("DOMContentLoaded", function () {
"div",
{
id: "qr-content",
+ onclose: () => {
+ general.returned_from_scanning = false;
+ },
oninit: () => {
helper.bottom_bar("", "", "");
- if (status == "after_scan") {
+ if (general.returned_from_scanning) {
helper.bottom_bar("", "", "");
- }
- if (url_test(qrcode_content)) {
- helper.bottom_bar("", "", "");
- }
+ if (url_test(qrcode_content)) {
+ helper.bottom_bar(
+ "",
+ "",
+ ""
+ );
+ }
- if (qrcode_content.startsWith("BEGIN:VCARD")) {
- let a = confirm(
- "Looks like it's a vCard, do you want to save it as a vcard file."
- );
- if (a) {
- let name = new Date() / 1000 + ".vcf";
- write_file(
- formatVCardContent(qrcode_content),
- "passport/" + name,
- "text/vcard"
+ if (url_test(qrcode_content)) {
+ helper.bottom_bar(
+ "",
+ "",
+ ""
);
}
+
+ if (qrcode_content.startsWith("BEGIN:VCARD")) {
+ vcard = true;
+ vcard_parser(qrcode_content);
+ }
+ }
+
+ if (
+ url_test(qrcode_content) &&
+ general.returned_from_scanning == false
+ ) {
+ helper.bottom_bar("", "", "");
}
},
},
@@ -699,7 +834,7 @@ document.addEventListener("DOMContentLoaded", function () {
let scan_callback = (e) => {
qrcode_content = e;
m.route.set("/show_qr_content");
- status = "after_scan";
+ general.returned_from_scanning = true;
};
var scan = {
@@ -720,11 +855,28 @@ document.addEventListener("DOMContentLoaded", function () {
},
};
+ //todo add textnote
+ var add_note = {
+ view: function () {
+ return m("div", [
+ m("video", {
+ id: "add_note",
+ oncreate: () => {
+ helper.bottom_bar("", "", "");
+ },
+ }),
+ m("input", { type: "text", id: "input-name" }),
+ m("input", { type: "text", id: "input-text" }),
+ ]);
+ },
+ };
+
m.route(root, "/start", {
"/show_qr_content": show_qr_content,
"/show_image": show_image,
"/show_pdf": show_pdf,
"/show_vcf": show_vcf,
+ "/add_note": add_note,
"/start": start,
"/options": options,
@@ -752,17 +904,20 @@ document.addEventListener("DOMContentLoaded", function () {
}
};
- let renameFile_callback = (filename) => {
+ let renameFile_callback = (filename, oldfile) => {
general.blocker = false;
+ files = files.filter((file) => !file.path.includes(oldfile));
+
let cb = () => {
m.route.set("/start?focus=+/sdcard/passport/" + filename);
};
- files = [];
+ //files = [];
read_files(cb);
};
let deleteFile_callback = (filename) => {
general.blocker = false;
+ files = files.filter((file) => !file.path.includes(filename));
let cb = () => {
if (files.length == 0) {
@@ -794,7 +949,7 @@ document.addEventListener("DOMContentLoaded", function () {
}
}
};
- files = [];
+ //files = [];
read_files(cb);
};
@@ -828,7 +983,8 @@ document.addEventListener("DOMContentLoaded", function () {
function longpress_action(param) {
switch (param.key) {
- case "*":
+ case "Backspace":
+ window.close();
break;
}
}
@@ -860,13 +1016,8 @@ document.addEventListener("DOMContentLoaded", function () {
m.route.set("/start");
} else if (m.route.get().includes("/scan")) {
m.route.set("/start");
- } else if (
- m.route.get().includes("/start") &&
- general.importAction == false
- ) {
- // window.close();
} else if (m.route.get().includes("/show_qr_content")) {
- if (status == "after_scan") {
+ if (general.returned_from_scanning) {
m.route.set("/start");
} else {
m.route.set("/show_image");
@@ -894,19 +1045,13 @@ document.addEventListener("DOMContentLoaded", function () {
case "Control":
if (m.route.get().includes("/show_image")) {
m.route.set("/show_qr_content");
- break;
- }
-
- if (m.route.get().includes("/show_pdf")) {
+ } else if (m.route.get().includes("/show_pdf")) {
zoomIn();
- break;
- }
-
- if (m.route.get().includes("/show_qr_content")) {
+ } else if (m.route.get().includes("/show_qr_content")) {
if (url_test(qrcode_content)) window.open(qrcode_content);
- }
-
- if (m.route.get().includes("/start")) {
+ } else if (m.route.get().includes("/show_vcf")) {
+ if (vcard_content.tel != "") mozactivity.dial(vcard_content.tel);
+ } else if (m.route.get().includes("/start")) {
if (general.fileAction) {
general.blocker = true;
let name = String(window.prompt("Enter name", ""));
@@ -924,13 +1069,42 @@ document.addEventListener("DOMContentLoaded", function () {
general.blocker = false;
}, 2000);
}
- } else {
- general.importAction = true;
+ }
+ if (general.importAction == false && general.blocker == false) {
helper.bottom_bar(
- "",
+ "",
"",
""
);
+
+ setTimeout(() => {
+ general.importAction = true;
+ }, 600);
+ }
+
+ if (general.importAction == true) {
+ try {
+ let h = (e) => {
+ general.blocker = false;
+
+ const b = {
+ fullName: (e.name && e.name[0]) || "",
+ tel: (e.tel && e.tel[0] && e.tel[0].value) || "",
+ email: (e.email && e.email[0] && e.email[0].value) || "",
+ };
+
+ create_vcard(b);
+ let name = b.fullName.replace(/\s+/g, "-");
+ write_file(
+ formatVCardContent(qrcode_content),
+ "passport/" + name + ".vcf",
+ "text/vcard"
+ );
+ };
+ mozactivity.pickContact(h);
+ } catch (e) {
+ console.log(e);
+ }
}
break;
@@ -941,9 +1115,9 @@ document.addEventListener("DOMContentLoaded", function () {
case "Alt":
if (m.route.get().includes("/show_pdf")) {
zoomOut();
- }
-
- if (m.route.get().includes("/start")) {
+ } else if (m.route.get().includes("/show_vcf")) {
+ if (vcard_content.tel != "") mozactivity.sms(vcard_content.tel);
+ } else if (m.route.get().includes("/start")) {
if (general.fileAction) {
let filePath = document.activeElement.getAttribute("data-path");
helper.deleteFile(filePath, deleteFile_callback);
@@ -968,9 +1142,17 @@ document.addEventListener("DOMContentLoaded", function () {
case "Enter":
if (m.route.get().includes("/show_qr_content")) {
- if (status != "") {
+ if (general.returned_from_scanning == true) {
generate_qr(qrcode_content);
}
+ if (vcard) {
+ let name = new Date() / 1000 + ".vcf";
+ write_file(
+ formatVCardContent(qrcode_content),
+ "passport/" + name,
+ "text/vcard"
+ );
+ }
}
if (
@@ -1037,6 +1219,10 @@ document.addEventListener("DOMContentLoaded", function () {
}
break;
+ case "3":
+ //m.route.set("/add_note");
+ break;
+
case "2":
if (m.route.get().includes("/start") && files.length > 0) {
general.fileAction = true;
diff --git a/application/manifest.webapp b/application/manifest.webapp
index a8ab165..2f0cf28 100644
--- a/application/manifest.webapp
+++ b/application/manifest.webapp
@@ -1,7 +1,7 @@
{
- "version": "1.2.12",
+ "version": "1.2.20",
"name": "passport",
- "description": "At times it is practically fast to access digital train airplane qrcode tickets to showcase at a check-in. I wrote an app for that. All images in the folder passport are listed.",
+ "description": "At times it is practically fast to access digital train airplane qrcode tickets to showcase at a check-in. I wrote an app for that. All All images,PDF's and vcards in the folder passport are listed.",
"launch_path": "/index.html",
"fullscreen": "true",
"lang": "en-US",
@@ -23,7 +23,7 @@
"en-US": {
"name": "Passport",
"subtitle": "Ticket please",
- "description": "At times it is practically fast to access digital train airplane qrcode tickets to showcase at a check-in. I wrote an app for that. All images in the folder passport are listed."
+ "description": "At times it is practically fast to access digital train airplane qrcode tickets to showcase at a check-in. I wrote an app for that. All images,PDF's and vcards in the folder passport are listed."
}
},
@@ -41,6 +41,7 @@
},
"mobiledata": {},
"wifidata": {},
- "calllog": {}
+ "calllog": {},
+ "contacts":{}
}
}
diff --git a/application/manifest.webmanifest b/application/manifest.webmanifest
index eabcdc6..f08b239 100644
--- a/application/manifest.webmanifest
+++ b/application/manifest.webmanifest
@@ -1,7 +1,7 @@
{
"name": "passport",
"id": "passport",
- "description": "At times it is practically fast to access digital train / airplane qrcode tickets to showcase at a check-in. I wrote an app for that. All images in the folder / passport / are listed.",
+ "description": "At times it is practically fast to access digital train / airplane qrcode tickets to showcase at a check-in. I wrote an app for that. All images,PDF's and vcards in the folder / passport / are listed.",
"lang": "en-US",
"start_url": "/index.html",
@@ -22,7 +22,7 @@
],
"b2g_features": {
- "version": "0.0.731",
+ "version": "0.0.743",
"id": "passport",
"core": true,
"categories": ["utilities"],
@@ -43,6 +43,8 @@
"mobiledata": {},
"wifidata": {},
"calllog": {},
+ "contacts": {},
+
"device-storage:sdcard": {
"description": "Read/Write from/to sd-card",
"access": "readwrite"
diff --git a/images/69e4e36be831b0e7461982a9cf969955-3763142281.jpg b/images/69e4e36be831b0e7461982a9cf969955-3763142281.jpg
deleted file mode 100644
index 71c4ba5..0000000
Binary files a/images/69e4e36be831b0e7461982a9cf969955-3763142281.jpg and /dev/null differ
diff --git a/images/Max-Klinger-1080x488.jpg b/images/Max-Klinger-1080x488.jpg
deleted file mode 100644
index 10284d2..0000000
Binary files a/images/Max-Klinger-1080x488.jpg and /dev/null differ
diff --git a/images/kaios-marketing-banner.png b/images/kaios-marketing-banner.png
index 940f2a8..5508518 100644
Binary files a/images/kaios-marketing-banner.png and b/images/kaios-marketing-banner.png differ