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