summaryrefslogtreecommitdiff
path: root/web-ui/app/js/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'web-ui/app/js/helpers')
-rw-r--r--web-ui/app/js/helpers/contenttype.js164
-rw-r--r--web-ui/app/js/helpers/iterator.js43
-rw-r--r--web-ui/app/js/helpers/triggering.js13
-rw-r--r--web-ui/app/js/helpers/view_helper.js145
4 files changed, 365 insertions, 0 deletions
diff --git a/web-ui/app/js/helpers/contenttype.js b/web-ui/app/js/helpers/contenttype.js
new file mode 100644
index 00000000..81519452
--- /dev/null
+++ b/web-ui/app/js/helpers/contenttype.js
@@ -0,0 +1,164 @@
+define([], function () {
+ var exports = {};
+
+ // Licence: PUBLIC DOMAIN <http://unlicense.org/>
+ // Author: Austin Wright <http://github.com/Acubed>
+
+ function MediaType(s, p){
+ this.type = '';
+ this.params = {};
+ if(typeof s=='string'){
+ var c = splitQuotedString(s);
+ this.type = c.shift();
+ for(var i=0; i<c.length; i++){
+ this.parseParameter(c[i]);
+ }
+ }else if(s instanceof MediaType){
+ this.type = s.type;
+ this.q = s.q;
+ for(var n in s.params) this.params[n]=s.params[n];
+ }
+ if(typeof p=='string'){
+ var c = splitQuotedString(p);
+ for(var i=0; i<c.length; i++){
+ this.parseParameter(c[i]);
+ }
+ }else if(typeof p=='object'){
+ for(var n in p) this.params[n]=p[n];
+ }
+ }
+ MediaType.prototype.parseParameter = function parseParameter(s){
+ var param = s.split('=',1);
+ var name = param[0].trim();
+ var value = s.substr(param[0].length+1).trim();
+ if(!value || !name) return;
+ if(name=='q' && this.q===undefined){
+ this.q=parseFloat(value);
+ }else{
+ if(value[0]=='"' && value[value.length-1]=='"'){
+ value = value.substr(1, value.length-2);
+ value = value.replace(/\\(.)/g, function(a,b){return b;});
+ }
+ this.params[name]=value;
+ }
+ }
+ MediaType.prototype.toString = function toString(){
+ var str = this.type + ';q='+this.q;
+ for(var n in this.params){
+ str += ';'+n+'=';
+ if(this.params[n].match(/["=;<>\[\]\(\) ,\-]/)){
+ str += '"' + this.params[n].replace(/["\\]/g, function(a){return '\\'+a;}) + '"';
+ }else{
+ str += this.params[n];
+ }
+ }
+ return str;
+ }
+ exports.MediaType = MediaType;
+
+ // Split a string by character, but ignore quoted parts and backslash-escaped characters
+ function splitQuotedString(str, delim, quote){
+ delim = delim || ';';
+ quote = quote || '"';
+ var res = [];
+ var start = 0;
+ var offset = 0;
+ function findNextChar(v, c, i, a){
+ var p = str.indexOf(c, offset+1);
+ return (p<0)?v:Math.min(p,v);
+ }
+ while(offset>=0){
+ offset = [delim,quote].reduce(findNextChar, 1/0);
+ if(offset===1/0) break;
+ switch(str[offset]){
+ case quote:
+ // Skip to end of quoted string
+ while(1){
+ offset=str.indexOf(quote, offset+1);
+ if(offset<0) break;
+ if(str[offset-1]==='\\') continue;
+ break;
+ }
+ continue;
+ case delim:
+ res.push(str.substr(start, offset-start).trim());
+ start = ++offset;
+ break;
+ }
+ }
+ res.push(str.substr(start).trim());
+ return res;
+ }
+ exports.splitQuotedString = splitQuotedString;
+
+ // Split a list of content types found in an Accept header
+ // Maybe use it like: splitContentTypes(request.headers.accept).map(parseMedia)
+ function splitContentTypes(str){
+ return splitQuotedString(str, ',');
+ }
+ exports.splitContentTypes = splitContentTypes;
+
+ function parseMedia(str){
+ var o = new MediaType(str);
+ if(o.q===undefined) o.q=1;
+ return o;
+ }
+ exports.parseMedia = parseMedia;
+
+ // Pick an ideal representation to send given a list of representations to choose from and the client-preferred list
+ function select(reps, accept){
+ var cr = {q:0};
+ var ca = {q:0};
+ var cq = 0;
+ for(var i=0; i<reps.length; i++){
+ var r = reps[i];
+ var rq = r.q || 1;
+ for(var j=0; j<accept.length; j++){
+ var a=accept[j];
+ var aq = a.q || 1;
+ var cmp = mediaCmp(a, r);
+ if(cmp!==null && cmp>=0){
+ if(aq*rq>cq){
+ ca = a;
+ cr = r;
+ cq = ca.q*cr.q;
+ if(cq===1 && cr.type) return cr;
+ }
+ }
+ }
+ }
+ return cr.type&&cr;
+ }
+ exports.select = select;
+
+ // Determine if one media type is a subset of another
+ // If a is a superset of b (b is smaller than a), return 1
+ // If b is a superset of a, return -1
+ // If they are the exact same, return 0
+ // If they are disjoint, return null
+ function mediaCmp(a, b){
+ if(a.type==='*/*' && b.type!=='*/*') return 1;
+ else if(a.type!=='*/*' && b.type==='*/*') return -1;
+ var ac = (a.type||'').split('/');
+ var bc = (b.type||'').split('/');
+ if(ac[0]=='*' && bc[0]!='*') return 1;
+ if(ac[0]!='*' && bc[0]=='*') return -1;
+ if(a.type!==b.type) return null;
+ var ap = a.params || {};
+ var bp = b.params || {};
+ var ak = Object.keys(ap);
+ var bk = Object.keys(bp);
+ if(ak.length < bk.length) return 1;
+ if(ak.length > bk.length) return -1;
+ var k = ak.concat(bk).sort();
+ var dir = 0;
+ for(var n in ap){
+ if(ap[n] && !bp[n]){ if(dir<0) return null; else dir=1; }
+ if(!ap[n] && bp[n]){ if(dir>0) return null; else dir=-1; }
+ }
+ return dir;
+ }
+ exports.mediaCmp = mediaCmp;
+
+ return exports;
+});
diff --git a/web-ui/app/js/helpers/iterator.js b/web-ui/app/js/helpers/iterator.js
new file mode 100644
index 00000000..9d8358a7
--- /dev/null
+++ b/web-ui/app/js/helpers/iterator.js
@@ -0,0 +1,43 @@
+define(function () {
+
+ return Iterator;
+
+ function Iterator(elems, startingIndex) {
+
+ this.index = startingIndex || 0;
+ this.elems = elems;
+
+ this.hasPrevious = function () {
+ return this.index != 0;
+ };
+
+ this.hasNext = function () {
+ return this.index < this.elems.length - 1;
+ };
+
+ this.previous = function () {
+ return this.elems[--this.index];
+ };
+
+ this.next = function () {
+ return this.elems[++this.index];
+ };
+
+ this.current = function () {
+ return this.elems[this.index];
+ };
+
+ this.hasElements = function () {
+ return this.elems.length > 0;
+ };
+
+ this.removeCurrent = function () {
+ var removed = this.current(),
+ toRemove = this.index;
+
+ !this.hasNext() && this.index--;
+ this.elems.remove(toRemove);
+ return removed;
+ };
+ }
+}); \ No newline at end of file
diff --git a/web-ui/app/js/helpers/triggering.js b/web-ui/app/js/helpers/triggering.js
new file mode 100644
index 00000000..7c8ae136
--- /dev/null
+++ b/web-ui/app/js/helpers/triggering.js
@@ -0,0 +1,13 @@
+define([], function() {
+ 'use strict';
+
+ return function(that, event, data, on) {
+ return function() {
+ if(on) {
+ that.trigger(on, event, data || {});
+ } else {
+ that.trigger(event, data || {});
+ }
+ };
+ };
+});
diff --git a/web-ui/app/js/helpers/view_helper.js b/web-ui/app/js/helpers/view_helper.js
new file mode 100644
index 00000000..3fa9edc1
--- /dev/null
+++ b/web-ui/app/js/helpers/view_helper.js
@@ -0,0 +1,145 @@
+define(
+ [
+ 'helpers/contenttype',
+ 'lib/html_whitelister',
+ 'views/i18n',
+ 'quoted-printable/quoted-printable'
+ ],
+ function(contentType, htmlWhitelister, i18n_lib, quotedPrintable) {
+ 'use strict';
+
+ function formatStatusClasses(ss) {
+ return _.map(ss, function(s) {
+ return 'status-' + s;
+ }).join(' ');
+ }
+
+ function addParagraphsToPlainText(plainTextBodyPart) {
+ return _.map(plainTextBodyPart.split('\n'), function (paragraph) {
+ return '<p>' + paragraph + '</p>';
+ }).join('');
+ }
+
+ function isQuotedPrintableBodyPart (bodyPart) {
+ return bodyPart.headers['Content-Transfer-Encoding'] && bodyPart.headers['Content-Transfer-Encoding'] === 'quoted-printable';
+ }
+
+ function getHtmlContentType (mail) {
+ return _.find(mail.availableBodyPartsContentType(), function (contentType) {
+ return contentType.indexOf('text/html') >= 0;
+ });
+ }
+
+ function getSanitizedAndDecodedMailBody (bodyPart) {
+ var body;
+
+ if (isQuotedPrintableBodyPart(bodyPart)) {
+ body = quotedPrintable.decode(bodyPart.body);
+ } else {
+ body = bodyPart.body;
+ }
+
+ return htmlWhitelister.sanitize(body, htmlWhitelister.tagPolicy);
+ }
+
+ function formatMailBody (mail) {
+ if (mail.isMailMultipartAlternative()) {
+ var htmlContentType;
+
+ htmlContentType = getHtmlContentType(mail);
+
+ if (htmlContentType) {
+ return $(getSanitizedAndDecodedMailBody(mail.getMailPartByContentType(htmlContentType)));
+ }
+
+ return $(addParagraphsToPlainText(mail.getMailMultiParts[0]));
+ }
+
+ return $(addParagraphsToPlainText(mail.body));
+
+ /*
+ var body;
+ // probably parse MIME parts and ugliness here
+ // content_type: "multipart/alternative; boundary="----=_Part_1115_17865397.1370312509342""
+ var mediaType = new contentType.MediaType(mail.header.content_type);
+ if(mediaType.type === 'multipart/alternative') {
+ var parsedBodyParts = getMailMultiParts(mail.body, mediaType);
+ var selectedBodyPart = getHtmlMailPart(parsedBodyParts) || getPlainTextMailPart(parsedBodyParts) || parsedBodyParts[0];
+ body = selectedBodyPart.body;
+
+ if (isQuotedPrintableBodyPart(selectedBodyPart)) {
+ body = quotedPrintable.decode(body);
+ }
+ } else {
+ body = addParagraphsToPlainText(mail.body);
+ }
+ return $(htmlWhitelister.sanitize(body, htmlWhitelister.tagPolicy));
+ */
+ }
+
+ function moveCaretToEnd(el) {
+ if (typeof el.selectionStart == "number") {
+ el.selectionStart = el.selectionEnd = el.value.length;
+ } else if (typeof el.createTextRange != "undefined") {
+ el.focus();
+ var range = el.createTextRange();
+ range.collapse(false);
+ range.select();
+ }
+ }
+
+ function fixedSizeNumber(num, size) {
+ var res = num.toString();
+ while(res.length < size) {
+ res = "0" + res;
+ }
+ return res;
+ }
+
+ function getFormattedDate(date){
+ var today = createTodayDate();
+ if (date.getTime() > today.getTime()) {
+ return fixedSizeNumber(date.getHours(), 2) + ":" + fixedSizeNumber(date.getMinutes(), 2);
+ } else {
+ return "" + date.getFullYear() + "-" + fixedSizeNumber(date.getMonth() + 1, 2) + "-" + fixedSizeNumber(date.getDate(), 2);
+ }
+ }
+
+ function createTodayDate() {
+ var today = new Date();
+ today.setHours(0);
+ today.setMinutes(0);
+ today.setSeconds(0);
+ return today;
+ }
+
+ function moveCaretToEndOfText() {
+ var self = this;
+
+ moveCaretToEnd(self);
+ window.setTimeout(function() {
+ moveCaretToEnd(self);
+ }, 1);
+ }
+
+ function quoteMail(mail) {
+ var quotedLines = _.map(mail.body.split('\n'), function (line) {
+ return '> ' + line;
+ });
+
+ return '\n\n' + quotedLines.join('\n');
+ }
+
+ function i18n(text) {
+ return i18n_lib.get(text);
+ }
+
+ return {
+ formatStatusClasses: formatStatusClasses,
+ formatMailBody: formatMailBody,
+ moveCaretToEndOfText: moveCaretToEndOfText,
+ getFormattedDate: getFormattedDate,
+ quoteMail: quoteMail,
+ i18n: i18n
+ };
+});