Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
55 lines
No EOL
12 KiB
JavaScript
55 lines
No EOL
12 KiB
JavaScript
;(function(exports){'use strict';var view=new WeakMap();var priv=new WeakMap();var data=new WeakMap();var events=new WeakMap();var relation=new WeakMap();var rdigit=/^\d/;function Recipient(opts){var number;opts=opts||{};this.name=String(opts.name||opts.number||'');this.number=(opts.number||'')+'';this.email=opts.email||'';this.editable=opts.editable||'true';this.role=this.editable?'textbox':'button';this.source=opts.source||'manual';this.type=opts.type||'';this.carrier=opts.carrier||'';this.className='recipient';this.isEmail=Settings.supportEmailRecipient&&Utils.isEmailAddress(this.number);this.isLookupable=opts.isLookupable||false;this.isInvalid=opts.isInvalid||false;this.isQuestionable=opts.isQuestionable||false;number=this.number[0]==='+'?this.number.slice(1):this.number;if(this.isEmail){this.className+=' email';}else if(this.source==='manual'&&!rdigit.test(number)){this.isQuestionable=true;}
|
|
if(this.isQuestionable||this.isInvalid){this.className+=' invalid';}}
|
|
Recipient.prototype.set=function(dict){var length=Recipient.FIELDS.length;var key;for(var i=0;i<length;i++){key=Recipient.FIELDS[i];this[key]=dict[key];}
|
|
return this;};Recipient.prototype.clone=function(){return new Recipient(this);};Recipient.FIELDS=['name','number','email','editable','source','type','carrier','isQuestionable','isInvalid','isLookupable'];function Recipients(setup){if(!setup){throw new Error('`setup` missing');}
|
|
if(!setup.template){throw new Error('`setup.template` missing');}
|
|
if(!setup.outer){throw new Error('`setup.outer` missing');}
|
|
if(!setup.inner){throw new Error('`setup.inner` missing');}
|
|
var list=[];Object.defineProperties(this,{length:{get:function(){return list.length;},set:function(value){var oldLength=list.length;list.length=value;if(value<oldLength){this.render();}
|
|
return value;}},list:{get:function(){return list.slice();}},numbers:{get:function(){return list.reduce(function(unique,recipient){var value=recipient.number||recipient.email;if(!recipient.isInvalid&&unique.indexOf(value)===-1){unique.push(value);}
|
|
return unique;},[]);}},inputValue:{get:function(){var node=document.getElementById(setup.inner).lastElementChild;return(node&&node.isPlaceholder)?node.textContent.trim():'';}}});data.set(this,list);events.set(this,{add:[],remove:[],modechange:[]});view.set(this,new Recipients.View(this,setup));}
|
|
Recipients.prototype.on=function(type,handler){var handlers=events.get(this);if(!handlers[type]){throw new Error('Invalid event type: '+type);}
|
|
handlers[type].push(handler);return this;};Recipients.prototype.off=function(type,handler){var handlers=events.get(this);if(!handlers[type]){throw new Error('Invalid event type: '+type);}
|
|
if(!handler){handlers[type].length=0;}else{handlers[type].splice(handlers[type].indexOf(handler),1);}
|
|
return this;};Recipients.prototype.emit=function(type){var handlers=events.get(this);var args=[].slice.call(arguments,1);var handler,stack;if(!handlers[type]){throw new Error('Invalid event type: '+type);}
|
|
stack=handlers[type].slice();if(stack.length){while((handler=stack.pop())){handler.apply(null,args);}}
|
|
return this;};Recipients.prototype.add=function(entry){var list=data.get(this);var added;if(entry.number===undefined){throw new Error('recipient entry missing number');}
|
|
entry.editable=entry.editable||'false';['name','number','email'].forEach(function(field){if(entry[field]){entry[field]=(entry[field]+'').trim();}});list.push(added=new Recipient(entry));this.emit('add',list.length,added.clone());this.render();return this;};Recipients.prototype.update=function(recipOrIndex,entry){var list=data.get(this);var index=typeof recipOrIndex==='number'?recipOrIndex:list.indexOf(recipOrIndex);if(index>-1){list[index].set(new Recipient(entry));}
|
|
return this.render();};Recipients.prototype.remove=function(recipOrIndex){var list=data.get(this);var index=typeof recipOrIndex==='number'?recipOrIndex:list.indexOf(recipOrIndex);if(index===-1){return this;}
|
|
list.splice(index,1);this.emit('remove',list.length);return this.render(index);};Recipients.prototype.render=function(){view.get(this).reset();return this;};Recipients.prototype.focus=function(){view.get(this).focus();return this;};Recipients.prototype.visible=function(type,opts){view.get(this).visible(type,opts||{});return this;};Recipients.View=function(owner,setup){var inner=document.getElementById(setup.inner);var outer=document.getElementById(setup.outer);var template=setup.template;var nodes=[];var clone;var outerCss=window.getComputedStyle(outer);priv.set(this,{owner:owner,inner:inner,outer:outer,template:template,active:null,nodes:nodes,state:{isTransitioning:false,visible:'singleline'},minHeight:parseInt(outerCss.getPropertyValue('min-height'),10),mode:'singleline-mode'});clone=inner.cloneNode(true);clone.innerHTML=template.interpolate(new Recipient());clone.firstElementChild.innerHTML='';Object.defineProperties(this,{last:{get:function(){return nodes[nodes.length-1];}},placeholder:{get:function(){var node=clone.firstElementChild.cloneNode(true);node.isPlaceholder=true;return node;}}});this.updateMode=Utils.debounce(this.updateMode,100);['click','keypress','keyup','blur','pan'].forEach(function(type){outer.addEventListener(type,this,false);},this);new GestureDetector(outer).startDetecting();this.reset();};Recipients.View.isFocusable=true;Recipients.View.prototype.reset=function(){this.clear().render();return this;};Recipients.View.prototype.clear=function(){priv.get(this).inner.textContent='';return this;};Recipients.View.prototype.render=function(opts){var view=priv.get(this);var nodes=view.nodes;var inner=view.inner;var template=view.template;var list=view.owner.list;var length=list.length;var html='';opts=opts||{};opts.addPlaceholder=opts.addPlaceholder===undefined?true:opts.addPlaceholder;if(inner.textContent){this.clear();}
|
|
for(var i=0;i<length;i++){html+=template.interpolate(list[i]);}
|
|
if(opts.editable){opts.addPlaceholder=false;html+=template.interpolate(opts.editable);}
|
|
if(html){inner.innerHTML=html;}
|
|
if(opts.addPlaceholder){inner.appendChild(this.placeholder);}
|
|
nodes.length=0;if(!list.length){nodes.push.apply(nodes,inner.children[0]);}else{nodes.push.apply(nodes,inner.children);}
|
|
nodes.forEach(function forEachNode(node,i){node.isPlaceholder=false;node.contentEditable=false;node.setAttribute('role','button');if(i===nodes.length-1){node.isPlaceholder=true;node.contentEditable=true;node.setAttribute('role','textbox');}else{relation.set(node,list[i]);}});if(view.state.visible==='singleline'&&nodes.length){inner.querySelector(':last-child').scrollIntoView(false);}
|
|
this.updateMode();return this;};Recipients.View.prototype.focus=function(node){var view=priv.get(this);var range=document.createRange();var selection=window.getSelection();if(Recipients.View.isFocusable){if(!node){node=view.inner.lastElementChild;if(!node.isPlaceholder){node=view.inner.appendChild(this.placeholder);}}
|
|
if(node&&node.isPlaceholder){node.contentEditable=true;node.focus();}
|
|
range.selectNodeContents(node);range.collapse(false);selection.removeAllRanges();selection.addRange(range);view.inner.scrollTop=view.inner.scrollHeight;}
|
|
this.updateMode();return this;};Recipients.View.prototype.updateMode=function(){var view=priv.get(this);var mode=view.inner.scrollHeight>view.minHeight?'multiline-mode':'singleline-mode';if(view.mode!==mode){view.mode=mode;if(mode==='singleline-mode'&&view.state.visible==='multiline'){this.visible('singleline',{refocus:this});}
|
|
view.owner.emit('modechange',mode);}};var rtype=/^(multi|single)line$/;Recipients.View.prototype.visible=function(type,opts){var view=priv.get(this);var state=view.state;var error='visible "type" (multiline or singleline)';opts=opts||{};if(!type){throw new Error('Missing '+error);}
|
|
if(!rtype.test(type)){throw new Error('Invalid '+error);}
|
|
if(state.visible!==type){state.isTransitioning=false;}
|
|
if(state.isTransitioning){return this;}
|
|
if(state.visible===type){return this;}
|
|
view.outer.addEventListener('transitionend',function te(){this.removeEventListener('transitionend',te);var last=view.inner.lastElementChild;var previous;if(Navigation.isCurrentPanel('composer')&&state.visible==='singleline'){while(last!==null&&last.isPlaceholder){previous=last.previousElementSibling;if(!last.textContent){last.parentNode.removeChild(last);}
|
|
last=previous;}
|
|
if(opts.refocus){opts.refocus.focus();}}else if(state.visible==='multiline'&&last!==null){last.scrollIntoView(true);}
|
|
state.isTransitioning=false;});state.visible=type;state.isTransitioning=true;view.outer.className=type;view.inner.className=type;return this;};Recipients.View.prototype.handleEvent=function(event){var view=priv.get(this);var owner=view.owner;var isPreventingDefault=false;var isAcceptedRecipient=false;var isEdittingRecipient=false;var isDeletingRecipient=false;var isLookupable=false;var target=event.target;var keyCode=event.keyCode;var editable='false';var lastElement=view.inner.lastElementChild;var typed,recipient,length,previous;Recipients.View.isFocusable=true;if(event.type==='keypress'||event.type==='keyup'){typed=target.textContent.trim();length=typed.length;}
|
|
switch(event.type){case'pan':if(view.state.visible==='singleline'&&view.inner.scrollHeight>view.minHeight){if(event.detail.absolute.dy>0){this.visible('multiline');}
|
|
event.preventDefault();event.stopPropagation();return;}
|
|
break;case'click':if(target.parentNode===view.inner){if(target.isPlaceholder){return;}
|
|
typed=lastElement&&lastElement.isPlaceholder?lastElement.textContent.trim():'';if(typed){owner.add({name:typed,number:typed,source:'manual'}).focus();}
|
|
recipient=relation.get(target);if(recipient){if(recipient.source==='contacts'){Recipients.View.prompts.remove(recipient,function(isConfirmed){if(isConfirmed){owner.remove(relation.get(target));this.reset();}
|
|
Recipients.View.isFocusable=true;this.focus();}.bind(this));}else{isEdittingRecipient=true;typed=recipient.number||recipient.name;editable='true';owner.remove(recipient);target=view.inner.lastElementChild;target.textContent=typed;this.visible('singleline',{refocus:target});}}}else{if(view.state.visible!=='singleline'){this.visible('singleline',{refocus:this});}else{this.focus();}
|
|
return;}
|
|
break;case'keyup':if(typed&&(typed[length-1]===';'||typed[length-1]===',')){isAcceptedRecipient=true;isPreventingDefault=true;typed=typed.replace(/[;,]$/,'');target.textContent=typed;}
|
|
if(!isAcceptedRecipient&&(typed&&typed.length>=1)){this.visible('singleline',{refocus:target});}
|
|
if(!typed&&keyCode===event.DOM_VK_BACK_SPACE){isPreventingDefault=true;isDeletingRecipient=true;}
|
|
this.updateMode();break;case'keypress':if(keyCode===event.DOM_VK_BACK_SPACE){previous=target.previousSibling;if(!typed&&previous){recipient=relation.get(previous);if(previous.dataset.source==='contacts'){isPreventingDefault=true;isDeletingRecipient=true;view.owner.remove(recipient);}else if(previous.dataset.source==='manual'){isEdittingRecipient=true;isPreventingDefault=true;target=previous;view.inner.removeChild(view.inner.lastElementChild);data.get(view.owner).pop();view.owner.emit('remove',data.get(view.owner).length);}}}
|
|
if((keyCode===13||keyCode===event.DOM_VK_ENTER)||(keyCode===event.DOM_VK_TAB)){if(typed){isPreventingDefault=true;if(target.parentNode===view.inner){if(keyCode!==event.DOM_VK_TAB){isAcceptedRecipient=true;isLookupable=true;target.textContent=typed;}}}}
|
|
break;}
|
|
if(isAcceptedRecipient){if(typed){view.owner.add({name:typed,number:typed,editable:editable,source:'manual',role:editable?'textbox':'button',isLookupable:isLookupable});this.render().focus();}}
|
|
if(isDeletingRecipient){this.render().focus();}
|
|
if(isEdittingRecipient){target.contentEditable=true;target.setAttribute('role','textbox');target.isPlaceholder=true;this.focus(target);}
|
|
if(isPreventingDefault){event.preventDefault();}};Recipients.View.prompts={remove:function(recipient,callback){callback=typeof callback==='function'?callback:()=>{};var bodyTemplate=SharedComponents.phoneDetails({number:recipient.number,type:recipient.type,carrier:recipient.carrier});var title=document.createElement('bdi');title.textContent=recipient.name||recipient.number;Utils.confirm({raw:bodyTemplate.toDocumentFragment()},{raw:title},{text:'remove',className:'danger'}).then(()=>callback(true),()=>callback(false));Recipients.View.isFocusable=false;}};exports.Recipients=Recipients;}(this)); |