Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
|
|
@ -0,0 +1 @@
|
|||
;;window.COMPONENTS_BASE_URL='/shared/elements/';
|
||||
373
assets/webapps/ftu.gaiamobile.org/shared/elements/gaia-header/dist/gaia-header.js
vendored
Normal file
|
|
@ -0,0 +1,373 @@
|
|||
;;(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.GaiaHeader=f()}})(function(){var define,module,exports;return(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){;(function(define){define(function(require,exports,module){'use strict';var debug=0?console.log.bind(console):function(){};var cache={};var MIN=16;var MAX=24;var BUFFER=3;module.exports=function(config){debug('font fit',config);var space=config.space-BUFFER;var min=config.min||MIN;var max=config.max||MAX;var text=trim(config.text);var fontSize=max;var textWidth;var font;do{font=config.font.replace(/\d+px/,fontSize+'px');textWidth=getTextWidth(text,font);}while(textWidth>space&&fontSize!==min&&fontSize--);return{textWidth:textWidth,fontSize:fontSize,overflowing:textWidth>space};};function getTextWidth(text,font){var ctx=getCanvasContext(font);var width=ctx.measureText(text).width;debug('got text width',width);return width;}
|
||||
function getCanvasContext(font){debug('get canvas context',font);var cached=cache[font];if(cached){return cached;}
|
||||
var canvas=document.createElement('canvas');canvas.setAttribute('moz-opaque','true');canvas.setAttribute('width','1px');canvas.setAttribute('height','1px');debug('created canvas',canvas);var ctx=canvas.getContext('2d',{willReadFrequently:true});ctx.font=font;return cache[font]=ctx;}
|
||||
function trim(text){return text.replace(/\s+/g,' ').trim();}});})(typeof define=='function'&&define.amd?define:(function(n,w){'use strict';return typeof module=='object'?function(c){c(require,exports,module);}:function(c){var m={exports:{}};c(function(n){return w[n];},m.exports,m);w[n]=m.exports;};})('font-fit',this));},{}],2:[function(require,module,exports){;(function(define){'use strict';define(function(require,exports,module){var textContent=Object.getOwnPropertyDescriptor(Node.prototype,'textContent');var innerHTML=Object.getOwnPropertyDescriptor(Element.prototype,'innerHTML');var removeAttribute=Element.prototype.removeAttribute;var setAttribute=Element.prototype.setAttribute;var noop=function(){};exports.register=function(name,props){var baseProto=getBaseProto(props.extends);var template=props.template||baseProto.templateString;var extensible=props.extensible=props.hasOwnProperty('extensible')?props.extensible:true;delete props.extends;if(template){if(extensible&&props.template){props.templateString=props.template;}
|
||||
var output=processCss(template,name);props.template=document.createElement('template');props.template.innerHTML=output.template;props.lightCss=output.lightCss;props.globalCss=props.globalCss||'';props.globalCss+=output.globalCss;}
|
||||
injectGlobalCss(props.globalCss);delete props.globalCss;var descriptors=mixin(props.attrs||{},base.descriptors);props._attrs=props.attrs;delete props.attrs;var proto=createProto(baseProto,props);Object.defineProperties(proto,descriptors);try{return document.registerElement(name,{prototype:proto});}catch(e){if(e.name!=='NotSupportedError'){throw e;}}};var base={properties:{GaiaComponent:true,attributeChanged:noop,attached:noop,detached:noop,created:noop,createdCallback:function(){if(this.dirObserver){addDirObserver();}
|
||||
injectLightCss(this);this.created();},attributeChangedCallback:function(name,from,to){var prop=toCamelCase(name);if(this._attrs&&this._attrs[prop]){this[prop]=to;}
|
||||
this.attributeChanged(name,from,to);},attachedCallback:function(){if(this.dirObserver){this.setInnerDirAttributes=setInnerDirAttributes.bind(null,this);document.addEventListener('dirchanged',this.setInnerDirAttributes);}
|
||||
this.attached();},detachedCallback:function(){if(this.dirObserver){document.removeEventListener('dirchanged',this.setInnerDirAttributes);}
|
||||
this.detached();},setupShadowRoot:function(){if(!this.template){return;}
|
||||
var node=document.importNode(this.template.content,true);this.createShadowRoot().appendChild(node);if(this.dirObserver){setInnerDirAttributes(this);}
|
||||
return this.shadowRoot;},setAttr:function(name,value){var internal=this.shadowRoot.firstElementChild;setAttribute.call(internal,name,value);setAttribute.call(this,name,value);},removeAttr:function(name){var internal=this.shadowRoot.firstElementChild;removeAttribute.call(internal,name);removeAttribute.call(this,name);}},descriptors:{textContent:{set:function(value){textContent.set.call(this,value);if(this.lightStyle){this.appendChild(this.lightStyle);}},get:function(){return textContent.get();}},innerHTML:{set:function(value){innerHTML.set.call(this,value);if(this.lightStyle){this.appendChild(this.lightStyle);}},get:innerHTML.get}}};var defaultPrototype=createProto(HTMLElement.prototype,base.properties);function getBaseProto(proto){if(!proto){return defaultPrototype;}
|
||||
proto=proto.prototype||proto;return!proto.GaiaComponent?createProto(proto,base.properties):proto;}
|
||||
function createProto(proto,props){return mixin(Object.create(proto),props);}
|
||||
var hasShadowCSS=(function(){var div=document.createElement('div');try{div.querySelector(':host');div.querySelector('::content');return true;}
|
||||
catch(e){return false;}})();var regex={shadowCss:/(?:\:host|\:\:content)[^{]*\{[^}]*\}/g,':host':/(?:\:host)/g,':host()':/\:host\((.+)\)(?: \:\:content)?/g,':host-context':/\:host-context\((.+)\)([^{,]+)?/g,'::content':/(?:\:\:content)/g};function processCss(template,name){var globalCss='';var lightCss='';if(!hasShadowCSS){template=template.replace(regex.shadowCss,function(match){var hostContext=regex[':host-context'].exec(match);if(hostContext){globalCss+=match.replace(regex['::content'],'').replace(regex[':host-context'],'$1 '+name+'$2').replace(/ +/g,' ');}else{lightCss+=match.replace(regex[':host()'],name+'$1').replace(regex[':host'],name).replace(regex['::content'],name);}
|
||||
return'';});}
|
||||
return{template:template,lightCss:lightCss,globalCss:globalCss};}
|
||||
function injectGlobalCss(css){if(!css){return;}
|
||||
var style=document.createElement('style');style.innerHTML=css.trim();headReady().then(function(){document.head.appendChild(style);});}
|
||||
function headReady(){return new Promise(function(resolve){if(document.head){return resolve();}
|
||||
window.addEventListener('load',function fn(){window.removeEventListener('load',fn);resolve();});});}
|
||||
function injectLightCss(el){if(hasShadowCSS){return;}
|
||||
var stylesheet=el.querySelector('style');if(!stylesheet){stylesheet=document.createElement('style');stylesheet.setAttribute('scoped','');stylesheet.appendChild(document.createTextNode(el.lightCss));el.appendChild(stylesheet);}
|
||||
el.lightStyle=stylesheet;}
|
||||
function toCamelCase(string){return string.replace(/-(.)/g,function replacer(string,p1){return p1.toUpperCase();});}
|
||||
var dirObserver;function setInnerDirAttributes(component){var dir=component.dir||document.dir;Array.from(component.shadowRoot.children).forEach(element=>{if(element.nodeName!=='STYLE'){element.dir=dir;}});}
|
||||
function addDirObserver(){if(dirObserver){return;}
|
||||
dirObserver=new MutationObserver(onChanged);dirObserver.observe(document.documentElement,{attributeFilter:['dir'],attributes:true});function onChanged(mutations){document.dispatchEvent(new Event('dirchanged'));}}
|
||||
function mixin(target,source){for(var key in source){target[key]=source[key];}
|
||||
return target;}});})(typeof define=='function'&&define.amd?define:(function(n,w){'use strict';return typeof module=='object'?function(c){c(require,exports,module);}:function(c){var m={exports:{}};c(function(n){return w[n];},m.exports,m);w[n]=m.exports;};})('gaia-component',this));},{}],3:[function(require,module,exports){(function(define){'use strict';define(function(require,exports,module){var base=window.GAIA_ICONS_BASE_URL||window.COMPONENTS_BASE_URL||'bower_components/';if(!document.documentElement){window.addEventListener('load',load);}else{load();}
|
||||
function load(){if(isLoaded()){return;}
|
||||
var link=document.createElement('link');link.rel='stylesheet';link.type='text/css';link.href=base+'gaia-icons/gaia-icons.css';document.head.appendChild(link);exports.loaded=true;}
|
||||
function isLoaded(){return exports.loaded||document.querySelector('link[href*=gaia-icons]')||document.documentElement.classList.contains('gaia-icons-loaded');}});})(typeof define=='function'&&define.amd?define:(function(n,w){'use strict';return typeof module=='object'?function(c){c(require,exports,module);}:function(c){var m={exports:{}};c(function(n){return w[n];},m.exports,m);w[n]=m.exports;};})('gaia-icons',this));},{}],4:[function(require,module,exports){;(function(define){'use strict';define(function(require,exports,module){var component=require('gaia-component');var fontFit=require('font-fit');require('gaia-icons');var debug=0?console.log.bind(console):function(){};const KNOWN_ACTIONS={menu:'menu',back:'back',close:'close'};const TITLE_FONT='italic 300 24px FiraSans';const TITLE_PADDING=10;const MINIMUM_FONT_SIZE_CENTERED=20;const MINIMUM_FONT_SIZE_UNCENTERED=16;const MAXIMUM_FONT_SIZE=23;module.exports=component.register('gaia-header',{extensible:false,dirObserver:true,created:function(){debug('created');this.setupShadowRoot();this.els={actionButton:this.shadowRoot.querySelector('.action-button'),titles:this.getElementsByTagName('h1')};this.els.actionButton.addEventListener('click',e=>this.onActionButtonClick(e));this.observer=new MutationObserver(this.onMutation.bind(this));this.ignoreDir=this.hasAttribute('ignore-dir');this.titleEnd=this.getAttribute('title-end');this.titleStart=this.getAttribute('title-start');this.noFontFit=this.getAttribute('no-font-fit');this.notFlush=this.hasAttribute('not-flush');this.action=this.getAttribute('action');this.unresolved={};this.pending={};this._resizeThrottlingId=null;this.onResize=this.onResize.bind(this);},attached:function(){debug('attached');this.runFontFitSoon();this.observerStart();window.addEventListener('resize',this.onResize);},detached:function(){debug('detached');window.removeEventListener('resize',this.onResize);this.observerStop();this.clearPending();},clearPending:function(){for(var key in this.pending){this.pending[key].clear();delete this.pending[key];}
|
||||
window.cancelAnimationFrame(this._resizeThrottlingId);this._resizeThrottlingId=null;},runFontFit:function(){debug('run font-fit');if(this.noFontFit){return Promise.resolve();}
|
||||
var titles=this.els.titles;var space=this.getTitleSpace();var styles=[].map.call(titles,el=>this.getTitleStyle(el,space));return this.setTitleStylesSoon(styles);},runFontFitSoon:function(){debug('run font-fit soon');if(this.pending.runFontFitSoon){return;}
|
||||
this.pending.runFontFitSoon=this.nextTick(()=>{delete this.pending.runFontFitSoon;this.runFontFit();});},getTitleStyle:function(el,space){debug('get el style',el,space);var text=el.textContent;var styleId=space.start+text+space.end+'#'+space.value;if(!text||!text.trim()){return debug('exit: no text');}
|
||||
if(getStyleId(el)===styleId){return debug('exit: no change');}
|
||||
var marginStart=this.getTitleMarginStart();var textSpace=space.value-Math.abs(marginStart);var fontFitResult=this.fontFit(text,textSpace,{min:MINIMUM_FONT_SIZE_CENTERED});var overflowing=fontFitResult.overflowing;var padding={start:0,end:0};if(overflowing){debug('title overflowing');padding.start=!space.start?TITLE_PADDING:0;padding.end=!space.end?TITLE_PADDING:0;textSpace=space.value-padding.start-padding.end;fontFitResult=this.fontFit(text,textSpace);marginStart=0;}
|
||||
return{id:styleId,fontSize:fontFitResult.fontSize,marginStart:marginStart,overflowing:overflowing,padding:padding};},setTitleStylesSoon:function(styles){debug('set title styles soon',styles);var key='setStyleTitlesSoon';this._titleStyles=styles;if(this.unresolved[key]){return this.unresolved[key];}
|
||||
this.unresolved[key]=new Promise((resolve)=>{this.pending[key]=this.nextTick(()=>{var styles=this._titleStyles;var els=this.els.titles;[].forEach.call(els,(el,i)=>{if(!styles[i]){return debug('exit');}
|
||||
this.setTitleStyle(el,styles[i]);});delete this._titleStyles;delete this.unresolved[key];delete this.pending[key];resolve();});});},setTitleStyle:function(el,style){debug('set title style',style);this.observerStop();if(this.ignoreDir){el.style.marginLeft=style.marginStart+'px';el.style.paddingLeft=style.padding.start+'px';el.style.paddingRight=style.padding.end+'px';}else{el.style.marginInlineStart=style.marginStart+'px';el.style.paddingInlineStart=style.padding.start+'px';el.style.paddingInlineEnd=style.padding.end+'px';}
|
||||
el.style.fontSize=style.fontSize+'px';setStyleId(el,style.id);this.observerStart();},fontFit:function(text,space,opts={}){debug('font fit:',text,space,opts);var fontFitArgs={font:TITLE_FONT,min:opts.min||MINIMUM_FONT_SIZE_UNCENTERED,max:MAXIMUM_FONT_SIZE,text:text,space:space};return fontFit(fontFitArgs);},observerStart:function(){if(this.observing){return;}
|
||||
this.observer.observe(this,{childList:true,attributes:true,subtree:true});this.observing=true;debug('observer started');},observerStop:function(){if(!this.observing){return;}
|
||||
this.observer.disconnect();this.observing=false;debug('observer stopped');},onResize:function(e){debug('onResize',this._resizeThrottlingId);if(this._resizeThrottlingId!==null){return;}
|
||||
this._resizeThrottlingId=window.requestAnimationFrame(()=>{this._resizeThrottlingId=null;this.runFontFitSoon();});},onMutation:function(mutations){debug('on mutation',mutations);if(!this.pending.runFontFitSoon){this.runFontFit();}},getTitleSpace:function(){var start=this.titleStart;var end=this.titleEnd;var space=this.getWidth()-start-end;var result={value:space,start:start,end:end};debug('get title space',result);return result;},getWidth:function(){var value=this.notFlush?this.clientWidth:window.innerWidth;debug('get width',value);return value;},triggerAction:function(){if(this.action){this.els.actionButton.click();}},onActionButtonClick:function(){debug('action button click');var config={detail:{type:this.action}};var e=new CustomEvent('action',config);setTimeout(()=>this.dispatchEvent(e));},getTitleMarginStart:function(){var start=this.titleStart;var end=this.titleEnd;var marginStart=end-start;debug('get title margin start',marginStart);return marginStart;},getButtonsBeforeTitle:function(){var children=this.children;var l=children.length;var els=[];for(var i=0;i<l;i++){var el=children[i];if(el.tagName==='H1'){break;}
|
||||
if(!contributesToLayout(el)){continue;}
|
||||
els.push(el);}
|
||||
if(this.action){els.push(this.els.actionButton);}
|
||||
return els;},getButtonsAfterTitle:function(){var children=this.children;var els=[];for(var i=children.length-1;i>=0;i--){var el=children[i];if(el.tagName==='H1'){break;}
|
||||
if(!contributesToLayout(el)){continue;}
|
||||
els.push(el);}
|
||||
return els;},sumButtonWidths:function(buttons){var defaultWidth=50;var sum=buttons.reduce((prev,button)=>{var isStandardButton=button===this.els.actionButton;var width=isStandardButton?defaultWidth:button.clientWidth;return prev+width;},0);debug('sum button widths',buttons,sum);return sum;},attrs:{action:{get:function(){return this._action;},set:function(value){var action=KNOWN_ACTIONS[value];if(action===this._action){return;}
|
||||
this.setAttr('action',action);this._action=action;}},titleStart:{get:function(){debug('get title-start');if('_titleStart'in this){return this._titleStart;}
|
||||
var buttons=this.getButtonsBeforeTitle();var value=this.sumButtonWidths(buttons);debug('get title-start',buttons,value);return value;},set:function(value){debug('set title-start',value);value=parseInt(value,10);if(value===this._titleStart||isNaN(value)){return;}
|
||||
this.setAttr('title-start',value);this._titleStart=value;debug('set');}},titleEnd:{get:function(){debug('get title-end');if('_titleEnd'in this){return this._titleEnd;}
|
||||
var buttons=this.getButtonsAfterTitle();return this.sumButtonWidths(buttons);},set:function(value){debug('set title-end',value);value=parseInt(value,10);if(value===this._titleEnd||isNaN(value)){return;}
|
||||
this.setAttr('title-end',value);this._titleEnd=value;}},noFontFit:{get:function(){return this._noFontFit||false;},set:function(value){debug('set no-font-fit',value);value=!!(value||value==='');if(value===this.noFontFit){return;}
|
||||
this._noFontFit=value;if(value){this.setAttr('no-font-fit','');}
|
||||
else{this.removeAttr('no-font-fit');}}},ignoreDir:{get:function(){return this._ignoreDir||false;},set:function(value){debug('set ignore-dir',value);value=!!(value||value==='');if(value===this.ignoreDir){return;}
|
||||
this._ignoreDir=value;this.dirObserver=!value;if(value){this.setAttr('ignore-dir','');}
|
||||
else{this.removeAttr('ignore-dir');}}}},template:`<div class="inner">
|
||||
<button class="action-button">
|
||||
<content select="[l10n-action]"></content>
|
||||
</button>
|
||||
<content></content>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
-moz-user-select: none;
|
||||
|
||||
--gaia-header-button-color:
|
||||
var(--header-button-color,
|
||||
var(--header-color,
|
||||
var(--link-color,
|
||||
inherit)));
|
||||
}
|
||||
|
||||
/**
|
||||
* [hidden]
|
||||
*/
|
||||
|
||||
:host[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/** Reset
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::-moz-focus-inner { border: 0; }
|
||||
|
||||
/** Inner
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.inner {
|
||||
display: flex;
|
||||
min-height: 50px;
|
||||
-moz-user-select: none;
|
||||
|
||||
background:
|
||||
var(--header-background,
|
||||
var(--background,
|
||||
#fff));
|
||||
}
|
||||
|
||||
/** Action Button
|
||||
---------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* 1. Hidden by default
|
||||
*/
|
||||
|
||||
.action-button {
|
||||
position: relative;
|
||||
|
||||
display: none; /* 1 */
|
||||
width: 50px;
|
||||
font-size: 30px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
|
||||
align-items: center;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
transition: opacity 200ms 280ms;
|
||||
color:
|
||||
var(--header-action-button-color,
|
||||
var(--header-icon-color,
|
||||
var(--gaia-header-button-color)));
|
||||
}
|
||||
|
||||
/**
|
||||
* [action=back]
|
||||
* [action=menu]
|
||||
* [action=close]
|
||||
*
|
||||
* 1. For icon vertical-alignment
|
||||
*/
|
||||
|
||||
[action=back] .action-button,
|
||||
[action=menu] .action-button,
|
||||
[action=close] .action-button {
|
||||
display: flex; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* :active
|
||||
*/
|
||||
|
||||
.action-button:active {
|
||||
transition: none;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
/** Action Button Icon
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.action-button:before {
|
||||
font-family: 'gaia-icons';
|
||||
font-style: normal;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
[action=close] .action-button:before { content: 'close' }
|
||||
[action=menu] .action-button:before { content: 'menu' }
|
||||
|
||||
[action=back]:-moz-dir(ltr) .action-button:before { content: 'left' }
|
||||
[action=back]:-moz-dir(rtl) .action-button:before { content: 'right' }
|
||||
|
||||
/** Action Button Icon
|
||||
---------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* 1. To enable vertical alignment.
|
||||
*/
|
||||
|
||||
.action-button:before {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/** Action Button Text
|
||||
---------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* To provide custom localized content for
|
||||
* the action-button, we allow the user
|
||||
* to provide an element with the class
|
||||
* .l10n-action. This node is then
|
||||
* pulled inside the real action-button.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* <gaia-header action="back">
|
||||
* <span l10n-action aria-label="Back">Localized text</span>
|
||||
* <h1>title</h1>
|
||||
* </gaia-header>
|
||||
*/
|
||||
|
||||
::content [l10n-action] {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
/** Title
|
||||
---------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* 1. Vertically center text. We can't use flexbox
|
||||
* here as it breaks text-overflow ellipsis
|
||||
* without an inner div.
|
||||
*/
|
||||
|
||||
::content h1 {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
line-height: 50px; /* 1 */
|
||||
font-weight: 300;
|
||||
font-style: italic;
|
||||
font-size: 24px;
|
||||
|
||||
color:
|
||||
var(--header-title-color,
|
||||
var(--header-color,
|
||||
var(--title-color,
|
||||
var(--text-color,
|
||||
inherit))));
|
||||
}
|
||||
|
||||
/**
|
||||
* [ignore-dir]
|
||||
*
|
||||
* When the <gaia-header> component has an [ignore-dir] attribute, header
|
||||
* direction is forced to LTR but we still want the <h1> text to be reversed
|
||||
* so that strings like '1 selected' become 'selected 1'.
|
||||
*
|
||||
* When we're happy for <gaia-header> to be fully RTL responsive we won't need
|
||||
* these rules anymore, but this depends on all Gaia apps being ready.
|
||||
*
|
||||
* This should be safe to remove when bug 1179459 lands.
|
||||
*/
|
||||
|
||||
:host[ignore-dir] {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
:host[ignore-dir]:-moz-dir(rtl) h1 {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
/** Buttons
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::content a,
|
||||
::content button {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
width: auto;
|
||||
height: auto;
|
||||
min-width: 50px;
|
||||
margin: 0;
|
||||
padding: 0 10px;
|
||||
outline: 0;
|
||||
border: 0;
|
||||
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
background: none;
|
||||
border-radius: 0;
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
transition: opacity 200ms 280ms;
|
||||
color: var(--gaia-header-button-color);
|
||||
}
|
||||
|
||||
/**
|
||||
* :active
|
||||
*/
|
||||
|
||||
::content a:active,
|
||||
::content button:active {
|
||||
transition: none;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
/**
|
||||
* [hidden]
|
||||
*/
|
||||
|
||||
::content a[hidden],
|
||||
::content button[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* [disabled]
|
||||
*/
|
||||
|
||||
::content a[disabled],
|
||||
::content button[disabled] {
|
||||
pointer-events: none;
|
||||
color: var(--header-disabled-button-color);
|
||||
}
|
||||
|
||||
/** Icon Buttons
|
||||
---------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Icons are a different color to text
|
||||
*/
|
||||
|
||||
::content .icon,
|
||||
::content [data-icon] {
|
||||
color:
|
||||
var(--header-icon-color,
|
||||
var(--gaia-header-button-color));
|
||||
}
|
||||
|
||||
/**
|
||||
* If users want their action button
|
||||
* to be in the component's light-dom
|
||||
* they can add an .action class
|
||||
* to make it look like the
|
||||
* shadow action button.
|
||||
*/
|
||||
|
||||
::content .action {
|
||||
color:
|
||||
var(--header-action-button-color,
|
||||
var(--header-icon-color,
|
||||
var(--gaia-header-button-color)));
|
||||
}
|
||||
|
||||
/**
|
||||
* [data-icon]:empty
|
||||
*
|
||||
* Icon buttons with no textContent,
|
||||
* should always be 50px.
|
||||
*
|
||||
* This is to prevent buttons being
|
||||
* larger than they should be before
|
||||
* icon-font has loaded.
|
||||
*/
|
||||
|
||||
::content [data-icon]:empty {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
</style>`,nextTick:nextTick});function contributesToLayout(el){return el.localName!=='style'&&!el.hasAttribute('l10n-action');}
|
||||
function setStyleId(el,id){el._styleId=id;}
|
||||
function getStyleId(el){return el._styleId;}
|
||||
function nextTick(fn){var cleared;Promise.resolve().then(()=>{if(!cleared){fn();}});return{clear:function(){cleared=true;}};}});})(typeof define=='function'&&define.amd?define:(function(n,w){'use strict';return typeof module=='object'?function(c){c(require,exports,module);}:function(c){var m={exports:{}};c(function(n){return w[n];},m.exports,m);w[n]=m.exports;};})('gaia-header',this));},{"font-fit":1,"gaia-component":2,"gaia-icons":3}]},{},[4])(4)});
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/* Generated by grunt-webfont */
|
||||
/* Based on https://github.com/endtwist/fontcustom/blob/master/lib/fontcustom/templates/fontcustom.css */
|
||||
|
||||
@font-face {
|
||||
font-family: "gaia-icons";
|
||||
src: url("fonts/gaia-icons.ttf") format("truetype");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
[data-icon]:before,
|
||||
.ligature-icons {
|
||||
font-family: "gaia-icons";
|
||||
content: attr(data-icon);
|
||||
display: inline-block;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
text-decoration: inherit;
|
||||
text-transform: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-size: 30px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
[data-icon]:not([data-l10n-id]):not([aria-label]):not([aria-hidden="true"]):before {
|
||||
/* Highlight cases where icons are not localized and still visible to screen
|
||||
reader */
|
||||
background: rgba(255, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
[data-icon]:not([data-l10n-id]):not([aria-label]):not([aria-hidden="true"]):after {
|
||||
background: #333;
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
width: 20rem;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
content: "Icon is not localized and is still visible to screen reader. Please add data-l10n-id or aria-label for localization or use aria-hidden to hide the icon from screen reader.";
|
||||
z-index: 9999;
|
||||
}
|
||||
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
Gaia theme variables
|
||||
*/
|
||||
|
||||
|
||||
:root {
|
||||
|
||||
/** Grey Colors
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--color-alpha: #333333;
|
||||
--color-beta: #ffffff;
|
||||
--color-gamma: #4d4d4d;
|
||||
--color-delta: #5f5f5f;
|
||||
--color-epsilon: #858585;
|
||||
--color-zeta: #a6a6a6;
|
||||
--color-eta: #c7c7c7;
|
||||
--color-theta: #e7e7e7;
|
||||
--color-iota: #f4f4f4;
|
||||
|
||||
/** Brand Colors
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--color-darkblue: #00539f;
|
||||
--color-blue: #00caf2;
|
||||
--color-turquoise: #27c8c2;
|
||||
--color-darkorange: #e66000;
|
||||
--color-orange: #ff9500;
|
||||
--color-yellow: #ffcb00;
|
||||
--color-violet: #c40c84;
|
||||
|
||||
--color-warning: #fbbd3c;
|
||||
--color-destructive: #e2443a;
|
||||
--color-preffered: #00ba91;
|
||||
|
||||
/** Background
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--background: var(--color-beta);
|
||||
--background-plus: var(--color-theta);
|
||||
--background-minus: var(--color-zeta);
|
||||
|
||||
/** Borders
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--border-color: var(--color-theta);
|
||||
|
||||
/** Text Color
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--text-color: var(--color-gamma);
|
||||
--text-color-minus: var(--color-eta);
|
||||
--label-color: #000;
|
||||
|
||||
/** Button
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--button-background: var(--color-iota);
|
||||
|
||||
/** Links
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--link-color: var(--highlight-color);
|
||||
|
||||
/** Inputs
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--input-background: var(--color-beta);
|
||||
--input-color: var(--color-alpha);
|
||||
--input-clear-background: #909ca7;
|
||||
|
||||
/** Buttons
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--button-box-shadow:
|
||||
inset 0 -1px 0 #d8d8d8,
|
||||
inset 0 1px 0 #eeeeee;
|
||||
--button-box-shadow-active: var(--button-box-shadow);
|
||||
|
||||
/** Header
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--header-disabled-button-color: rgba(255,255,255,0.3);
|
||||
|
||||
/** Transitions
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--transition-duration: 0.2s;
|
||||
}
|
||||
|
||||
|
||||
.theme-productivity {
|
||||
|
||||
/** Brand Color
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--highlight-color: var(--color-orange);
|
||||
|
||||
/** Header
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--header-background: var(--highlight-color);
|
||||
--header-color: var(--color-beta);
|
||||
}
|
||||
|
||||
.theme-communications {
|
||||
|
||||
/** Brand Color
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--highlight-color: var(--color-turquoise);
|
||||
|
||||
/** Links
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--link-color: #177874;
|
||||
|
||||
/** Header
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--header-background: var(--highlight-color);
|
||||
--header-color: var(--color-beta);
|
||||
--header-icon-color: var(--color-beta);
|
||||
--header-button-color: var(--link-color);
|
||||
}
|
||||
|
||||
.theme-settings {
|
||||
|
||||
/** Background
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--background: var(--color-iota);
|
||||
--background-plus: var(--color-beta);
|
||||
--background-minus: var(--color-epsilon);
|
||||
--background-minus-minus: var(--color-zeta);
|
||||
|
||||
/** Highlight
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--highlight-color: var(--color-blue);
|
||||
|
||||
/** Borders
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--border-color: var(--color-theta);
|
||||
|
||||
/** Text Color
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--text-color: var(--color-gamma);
|
||||
|
||||
/** Titles
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--title-color: var(--background-minus);
|
||||
|
||||
/** Buttons
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--button-background: var(--background-plus);
|
||||
|
||||
/** Switch
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--switch-background: var(--background-minus-minus);
|
||||
|
||||
/** Checkbox
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--checkbox-border-color: var(--background-minus-minus);
|
||||
|
||||
/** Header
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--header-color: var(--title-color);
|
||||
--header-background: var(--background);
|
||||
--header-button-color: var(--highlight-color);
|
||||
--header-disabled-button-color: var(--color-theta);
|
||||
--header-action-button-color: var(--title-color);
|
||||
}
|
||||
|
||||
.theme-media {
|
||||
|
||||
/** Highlight Color
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--highlight-color: var(--color-blue);
|
||||
|
||||
/** Background
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--background: var(--color-alpha);
|
||||
--background-plus: var(--color-gamma);
|
||||
--background-minus: #2B2B2B;
|
||||
--background-minus-minus: #1a1a1a;
|
||||
|
||||
/** Text Color
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--text-color: var(--color-beta);
|
||||
--label-color: var(--color-beta);
|
||||
|
||||
/** Button
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--button-background: var(--background-plus);
|
||||
|
||||
/** Header
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--header-background: var(--background);
|
||||
--header-icon-color: var(--text-color);
|
||||
--header-button-color: var(--highlight-color);
|
||||
|
||||
/** Borders
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--border-color: var(--color-gamma);
|
||||
|
||||
/** Inputs
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--input-background: var(--background-plus);
|
||||
|
||||
/** Text Input
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--text-input-background: var(--background-minus);
|
||||
|
||||
/** Switch
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--switch-head-border-color: var(--background-minus-minus);
|
||||
--switch-background: var(--background-minus-minus);
|
||||
|
||||
/** Checkbox
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--checkbox-border-color: var(--background-minus-minus);
|
||||
|
||||
/** Buttons
|
||||
---------------------------------------------------------*/
|
||||
|
||||
--button-box-shadow: none;
|
||||
--button-box-shadow-active: none;
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 30 30"><path fill="#667174" fill-opacity=".7" d="m 18.2995,19.66 -5.056,-5.057 5.056,-5.056 c -0.31,-0.516 -0.62,-1.032 -0.93,-1.34 l -5.672,5.673 v 1.652 l 5.675,5.676 c 0.31,-0.515 0.722,-0.928 0.93,-1.547 z"/></svg>
|
||||
|
After Width: | Height: | Size: 294 B |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><path fill="#667174" fill-opacity=".7" d="M11.8,19.9l4.9-4.9l-4.9-4.9c0.3-0.5,0.6-1,0.9-1.3l5.5,5.5c0,0.3,0,0.5,0,0.8c0,0.3,0,0.5,0,0.8l-5.5,5.5 C12.4,20.9,12,20.5,11.8,19.9z"/></svg>
|
||||
|
After Width: | Height: | Size: 243 B |
|
|
@ -0,0 +1 @@
|
|||
;;'use strict';window.GaiaButtons=(function(win){var proto=Object.create(HTMLElement.prototype);var baseurl=window.GaiaButtonsBaseurl||'/shared/elements/gaia_buttons/';proto.createdCallback=function(){ComponentUtils.style.call(this,baseurl);};return document.registerElement('gaia-buttons',{prototype:proto});})(window);
|
||||
|
|
@ -0,0 +1,281 @@
|
|||
/* ---------------------------------------
|
||||
* gaia-buttons:
|
||||
* Container for buttons e.g:
|
||||
* action menu, confirm, edit mode, FTU
|
||||
* --------------------------------------- */
|
||||
|
||||
gaia-buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 7rem;
|
||||
box-sizing: border-box;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
gaia-buttons:not(.inline) {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 10;
|
||||
border-top: 0.1rem solid #e7e7e7;
|
||||
padding: 0;
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
/* this 'end' padding must match the buttons 'start' margin */
|
||||
gaia-buttons:not(.inline):-moz-dir(ltr) { padding-right: 1.5rem; }
|
||||
gaia-buttons:not(.inline):-moz-dir(rtl) { padding-left: 1.5rem; }
|
||||
|
||||
gaia-buttons.inline a,
|
||||
gaia-buttons.inline button {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button::-moz-focus-inner {
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
a,
|
||||
button {
|
||||
position: relative;
|
||||
font-family: sans-serif;
|
||||
font-style: italic;
|
||||
width: 100%;
|
||||
height: 4rem;
|
||||
margin: 0;
|
||||
padding: 0 0.2rem;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
background: #d8d8d8;
|
||||
border: none;
|
||||
border-radius: 2rem;
|
||||
font-weight: normal;
|
||||
font-size: 1.6rem;
|
||||
line-height: 4rem;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
}
|
||||
:-moz-dir(ltr) a,
|
||||
:-moz-dir(ltr) button {
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
:-moz-dir(rtl) a,
|
||||
:-moz-dir(rtl) button {
|
||||
margin-right: 1.5rem;
|
||||
}
|
||||
|
||||
/* Recommend */
|
||||
a.recommend,
|
||||
button.recommend {
|
||||
background-color: #00caf2;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Danger */
|
||||
a.danger,
|
||||
button.danger {
|
||||
background-color: #e51e1e;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Pressed */
|
||||
a:active,
|
||||
button:active {
|
||||
background-color: #b2f2ff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
a::-moz-selection,
|
||||
button::-moz-selection {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* Disabled */
|
||||
a[aria-disabled="true"],
|
||||
button[disabled] {
|
||||
background-color: rgba(0,0,0,0.05);
|
||||
color: #c7c7c7;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
a[aria-disabled="true"].recommend,
|
||||
button[disabled].recommend {
|
||||
background-color: #97cbd5;
|
||||
color: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
a[aria-disabled="true"].danger,
|
||||
button[disabled].danger {
|
||||
background-color: #eb7474;
|
||||
color: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
a[aria-disabled="true"]::-moz-selection,
|
||||
button[disabled]::-moz-selection {
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
/* Hidden */
|
||||
[hidden] {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/* Icons */
|
||||
a.icon,
|
||||
button.icon {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
a.icon:before,
|
||||
button.icon:before {
|
||||
content: "";
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -1.5rem;
|
||||
background: transparent no-repeat center center / 100% auto;
|
||||
}
|
||||
:-moz-dir(ltr) a.icon:before,
|
||||
:-moz-dir(ltr) button.icon:before { right: 0.5rem; }
|
||||
:-moz-dir(rtl) a.icon:before,
|
||||
:-moz-dir(rtl) button.icon:before { left: 0.5rem; }
|
||||
|
||||
a.icon-next:before,
|
||||
button.icon-next:before {
|
||||
background-size: 3rem;
|
||||
}
|
||||
:-moz-dir(ltr) a.icon-next:before,
|
||||
:-moz-dir(ltr) button.icon-next:before {
|
||||
background-image: url(images/right-light.svg);
|
||||
}
|
||||
:-moz-dir(rtl) a.icon-next:before,
|
||||
:-moz-dir(rtl) button.icon-next:before {
|
||||
background-image: url(images/left-light.svg);
|
||||
}
|
||||
|
||||
a.icon-previous:before,
|
||||
button.icon-previous:before {
|
||||
background-size: 3rem;
|
||||
}
|
||||
:-moz-dir(ltr) a.icon-previous:before,
|
||||
:-moz-dir(ltr) button.icon-previous:before {
|
||||
left: 0.5rem;
|
||||
background-image: url(images/left-light.svg);
|
||||
}
|
||||
:-moz-dir(rtl) a.icon-previous:before,
|
||||
:-moz-dir(rtl) button.icon-previous:before {
|
||||
right: 0.5rem;
|
||||
background-image: url(images/right-light.svg);
|
||||
}
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
SKIN: dark
|
||||
----------------------------------------------------------------- */
|
||||
|
||||
gaia-buttons[skin="dark"] {
|
||||
background: #4d4d4d;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Pressed */
|
||||
[skin="dark"] a:active,
|
||||
[skin="dark"] button:active {
|
||||
background-color: #00aacc;
|
||||
}
|
||||
|
||||
/* Disabled */
|
||||
[skin="dark"] a[aria-disabled="true"],
|
||||
[skin="dark"] button[disabled] {
|
||||
background-color: #565656;
|
||||
color: rgba(255,255,255,0.4);
|
||||
}
|
||||
|
||||
[skin="dark"] a[aria-disabled="true"].recommend,
|
||||
[skin="dark"] button[disabled].recommend {
|
||||
background-color: #006579;
|
||||
}
|
||||
|
||||
[skin="dark"] a[aria-disabled="true"].danger,
|
||||
[skin="dark"] button[disabled].danger {
|
||||
background-color: #730f0f;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
Transitions
|
||||
----------------------------------------------------------------- */
|
||||
|
||||
[transition] {
|
||||
display: flex;
|
||||
transition: visibility var(--transition-duration);
|
||||
}
|
||||
|
||||
[transition][hidden] {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
[transition="slide"] {
|
||||
transform: translateY(0);
|
||||
transition: transform var(--transition-duration) ease,
|
||||
visibility var(--transition-duration);
|
||||
}
|
||||
|
||||
[transition="slide"][hidden] {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
[transition="fade"] {
|
||||
opacity: 1;
|
||||
transition: opacity var(--transition-duration) ease,
|
||||
visibility var(--transition-duration);
|
||||
}
|
||||
|
||||
[transition="fade"][hidden] {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
RESPONSIVE: tablet
|
||||
----------------------------------------------------------------- */
|
||||
|
||||
@media (min-width: 768px) {
|
||||
|
||||
gaia-buttons {
|
||||
height: 10rem;
|
||||
}
|
||||
|
||||
gaia-buttons:not(.inline) {
|
||||
border-top-width: 0.2rem;
|
||||
}
|
||||
/* this 'end' padding must match the buttons 'start' margin */
|
||||
gaia-buttons:not(.inline):-moz-dir(ltr) { padding-right: 3rem; }
|
||||
gaia-buttons:not(.inline):-moz-dir(rtl) { padding-left: 3rem; }
|
||||
|
||||
a,
|
||||
button {
|
||||
font-size: 2rem;
|
||||
height: 5rem;
|
||||
line-height: 5rem;
|
||||
border-radius: 2.5rem;
|
||||
max-width: 40rem;
|
||||
}
|
||||
:-moz-dir(ltr) a,
|
||||
:-moz-dir(ltr) button {
|
||||
margin-left: 3rem;
|
||||
}
|
||||
:-moz-dir(rtl) a,
|
||||
:-moz-dir(rtl) button {
|
||||
margin-right: 3rem;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 583 B |
|
After Width: | Height: | Size: 583 B |
|
|
@ -0,0 +1,5 @@
|
|||
;'use strict';window.GaiaCheckbox=(function(win){var proto=Object.create(HTMLElement.prototype);var baseurl=window.GaiaCheckboxBaseurl||'/shared/elements/gaia_checkbox/';proto.createdCallback=function(){this.lastClick=0;this.throttleTime=250;var shadow=this.createShadowRoot();this._template=template.content.cloneNode(true);this._wrapper=this._template.getElementById('checkbox');this._wrapper.addEventListener('click',this.handleClick.bind(this));this.configureClass();this.checked=this.hasAttribute('checked');this._wrapper.setAttribute('aria-checked',this.checked);this.setAttribute('role','presentation');shadow.appendChild(this._template);ComponentUtils.style.call(this,baseurl);};proto.handleClick=function(e){e.preventDefault();e.stopImmediatePropagation();if(this.lastClick+this.throttleTime>Date.now()){return;}
|
||||
this.lastClick=Date.now();var event=new MouseEvent('click',{view:window,bubbles:true,cancelable:true});this.dispatchEvent(event);if(!event.defaultPrevented){this.checked=!this.checked;this._wrapper.setAttribute('aria-checked',this.checked);}
|
||||
this.dispatchEvent(new CustomEvent('change',{bubbles:true,cancelable:false}));};proto.configureClass=function(){this._wrapper.className=this.className;};proto.attributeChangedCallback=function(name,from,to){if(name==='class'){this._wrapper.className=to;}};Object.defineProperty(proto,'checked',{get:function(){return this._checked||false;},set:function(value){this._wrapper.classList.toggle('checked',value);this._checked=value;}});Object.defineProperty(proto,'name',{get:function(){return this.getAttribute('name');},set:function(value){this.setAttribute('name',value);}});var template=document.createElement('template');template.innerHTML=`<span id="checkbox" role="checkbox">
|
||||
<span role="presentation"><content select="label"></content></span>
|
||||
</span>`;return document.registerElement('gaia-checkbox',{prototype:proto});})(window);
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
gaia-checkbox {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#checkbox {
|
||||
display: block;
|
||||
width: 100%;
|
||||
/* Normal gaia-checkboxes are the heights of their parents. */
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background: none;
|
||||
}
|
||||
|
||||
#checkbox span {
|
||||
font-size: 1.9rem;
|
||||
font-weight: normal;
|
||||
color: var(--label-color);
|
||||
line-height: 3rem;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
min-width: 2.2rem;
|
||||
min-height: 2.2rem;
|
||||
width: 100%;
|
||||
padding-inline-end: 2.2rem;
|
||||
/* Override inheriting nowrap from legacy lists building blocks. */
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/* Inline gaia-checkboxes are the heights of their parents. */
|
||||
#checkbox.inline {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#checkbox:not(.invisible) span:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
offset-inline-end: 0;
|
||||
top: 50%;
|
||||
margin: -1.2rem 0 0;
|
||||
margin-inline-start: -1.2rem;
|
||||
width: 2.2rem;
|
||||
height: 2.2rem;
|
||||
}
|
||||
|
||||
#checkbox.inline span {
|
||||
padding-inline-start: 3rem;
|
||||
padding-inline-end: 0;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
#checkbox.inline span:after {
|
||||
offset-inline-start: 1.2rem;
|
||||
offset-inline-end: unset;
|
||||
margin: -1.2rem 0 0;
|
||||
margin-inline-start: -1.2rem;
|
||||
}
|
||||
|
||||
#checkbox:not(.invisible) span:after {
|
||||
background: url(images/default.png) no-repeat center top / 2.2rem auto;
|
||||
}
|
||||
|
||||
#checkbox.checked span:after {
|
||||
background-position: center bottom;
|
||||
}
|
||||
|
||||
/* 'Dangerous' checkboxes */
|
||||
#checkbox.danger span:after {
|
||||
background-image: url(images/danger.png);
|
||||
}
|
||||
|
After Width: | Height: | Size: 292 B |
|
After Width: | Height: | Size: 578 B |
|
After Width: | Height: | Size: 575 B |
|
|
@ -0,0 +1,9 @@
|
|||
;'use strict';window.GaiaRadio=(function(win){var proto=Object.create(HTMLElement.prototype);var baseurl=window.GaiaRadioBaseurl||'/shared/elements/gaia_radio/';proto.createdCallback=function(){var shadow=this.createShadowRoot();this._template=template.content.cloneNode(true);this._wrapper=this._template.getElementById('radio');this._wrapper.addEventListener('click',this.handleClick.bind(this));if(this.dataset.role){this._wrapper.setAttribute('role',this.dataset.role);}
|
||||
if(!this.hasAttribute('role')){this.setAttribute('role','presentation');}
|
||||
this.configureClass();shadow.appendChild(this._template);this.checked=this.hasAttribute('checked');this._wrapper.setAttribute('aria-checked',this.checked);setTimeout(function nextTick(){var label=this.querySelector('label, p');if(!label){return;}
|
||||
label.addEventListener('click',this.handleClick.bind(this));}.bind(this));ComponentUtils.style.call(this,baseurl);var dirObserver=new MutationObserver(this.updateInternalDir.bind(this));dirObserver.observe(document.documentElement,{attributeFilter:['dir'],attributes:true});this.updateInternalDir();};proto.updateInternalDir=function(){var internal=this.shadowRoot.firstElementChild;if(document.documentElement.dir==='rtl'){internal.setAttribute('dir','rtl');}else{internal.removeAttribute('dir');}};proto.handleClick=function(e){e.stopImmediatePropagation();var event=new MouseEvent('click',{view:window,bubbles:true,cancelable:true});this.dispatchEvent(event);if(this.checked){return;}
|
||||
if(!event.defaultPrevented){this.checked=!this.checked;}
|
||||
this.dispatchEvent(new CustomEvent('change',{bubbles:true,cancelable:false}));};proto.configureClass=function(){this._wrapper.className=this.className;this._wrapper.classList.toggle('checked',this._checked);};proto.hideDetails=function(){this.shadowRoot.querySelector('.details').style.display='none';};proto.attributeChangedCallback=function(name,from,to){if(name==='class'){this._wrapper.className=to;}};Object.defineProperty(proto,'checked',{get:function(){return this._checked;},set:function(value){if(!this._template){return;}
|
||||
if(value){var relevant=document.querySelectorAll('gaia-radio[name="'+this.getAttribute('name')+'"]');for(var i=0,iLen=relevant.length;i<iLen;i++){relevant[i].checked=false;}
|
||||
this.setAttribute('checked',true);}else{this.removeAttribute('checked');}
|
||||
this._wrapper.classList.toggle('checked',value);this._wrapper.setAttribute('aria-checked',value);this._checked=value;}});Object.defineProperty(proto,'name',{get:function(){return this.getAttribute('name');},set:function(value){this.setAttribute('name',value);}});Object.defineProperty(proto,'value',{get:function(){return this.getAttribute('value');},set:function(value){this.setAttribute('value',value);}});Object.defineProperty(proto,'type',{get:function(){return'gaia-radio';}});var template=document.createElement('template');template.innerHTML='<span role="radio" id="radio">'+'<span><content select="p,label"></content></span>'+'<div class="details"><content select="details"></content></div>'+'<div class="divider"></div>'+'</span>';return document.registerElement('gaia-radio',{prototype:proto});})(window);
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
gaia-radio {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#radio {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
background: none;
|
||||
}
|
||||
|
||||
gaia-radio.inline,
|
||||
#radio.inline {
|
||||
width: 5rem;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#radio span {
|
||||
font-size: 1.9rem;
|
||||
color: var(--label-color);
|
||||
padding: 1rem 0;
|
||||
padding-inline-end: 2.4rem;
|
||||
line-height: 3rem;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
/* Override inheriting nowrap from legacy lists building blocks. */
|
||||
white-space: normal;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/* Users may choose to truncate text instead of it wrapping by adding the "truncate" className */
|
||||
#radio.truncate span {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#radio span:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
offset-inline-end: 0;
|
||||
top: 50%;
|
||||
margin: -1.1rem 0 0;
|
||||
margin-inline-start: -1.1rem;
|
||||
width: 2.2rem;
|
||||
height: 2.2rem;
|
||||
background: url(images/default.png) no-repeat center top / 2.2rem auto;
|
||||
}
|
||||
|
||||
#radio.checked span:after {
|
||||
background-position: center bottom;
|
||||
}
|
||||
|
||||
/* 'Dangerous' radio */
|
||||
#radio.danger span:after {
|
||||
background-image: url(images/danger.png);
|
||||
}
|
||||
|
||||
/* 'checkone' radio */
|
||||
#radio.checkone span:after {
|
||||
background: none;
|
||||
}
|
||||
|
||||
#radio.checkone.checked span:after {
|
||||
background: url(images/checked.png) no-repeat right center / 100%;
|
||||
}
|
||||
|
||||
#radio.checkone.checked span {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 'split' radio label */
|
||||
#radio.split .divider {
|
||||
content: '';
|
||||
position: absolute;
|
||||
background-color: #E6E6E3;
|
||||
offset-inline-end: 3.5rem;
|
||||
width: 0.1rem;
|
||||
height: calc(100% - 1rem);
|
||||
top: 0.5rem;
|
||||
}
|
||||
|
||||
gaia-radio.split label {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* A <details> slot may be specified in addition the the <label>.
|
||||
* When provided, it appears under the <label> and can provide additional context.
|
||||
*/
|
||||
#radio div.details {
|
||||
position: relative;
|
||||
top: -1.5rem;
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding-top: 0.5rem;
|
||||
-moz-padding-start: 0;
|
||||
-moz-padding-end: 8rem;
|
||||
font-size: 1.4rem;
|
||||
line-height: 1.6rem;
|
||||
color: #505859;
|
||||
font-style: normal;
|
||||
white-space: normal;
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
;'use strict';window.GaiaSubheader=(function(win){var proto=Object.create(HTMLElement.prototype);var baseurl=window.GaiaSubheaderBaseurl||'/shared/elements/gaia_subheader/';proto.createdCallback=function(){ComponentUtils.style.call(this,baseurl);};return document.registerElement('gaia-subheader',{prototype:proto});})(window);
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
gaia-subheader[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gaia Subheader
|
||||
*
|
||||
* Dividing header usually found
|
||||
* heading and separating lists.
|
||||
*/
|
||||
|
||||
gaia-subheader {
|
||||
display: block;
|
||||
padding: 0.8rem 3rem;
|
||||
border-bottom: solid 0.1rem #e6e6e6;
|
||||
color: #424242;
|
||||
font-size: 1.5rem;
|
||||
line-height: 1.8rem;
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
/**
|
||||
* [data-skin="organic"]
|
||||
*/
|
||||
|
||||
gaia-subheader[skin="organic"] {
|
||||
border: 0;
|
||||
background-color: #e7e7e7;
|
||||
color: #4d4d4d;
|
||||
}
|
||||
|
||||
/**
|
||||
* [data-skin="dark"]
|
||||
*/
|
||||
|
||||
gaia-subheader[skin="dark"] {
|
||||
border: 0;
|
||||
background-color: #2c353b;
|
||||
color: #fff;
|
||||
}
|
||||
|
After Width: | Height: | Size: 755 B |
|
After Width: | Height: | Size: 475 B |
|
After Width: | Height: | Size: 748 B |
|
|
@ -0,0 +1,9 @@
|
|||
;'use strict';window.GaiaSwitch=(function(win){var proto=Object.create(HTMLElement.prototype);var baseurl=window.GaiaSwitchBaseurl||'/shared/elements/gaia_switch/';proto.createdCallback=function(){var shadow=this.createShadowRoot();this._template=template.content.cloneNode(true);this._input=this._template.querySelector('input[type="checkbox"]');var checked=this.getAttribute('checked');if(checked!==null){this._input.checked=true;}
|
||||
var wrapper=this._template.getElementById('switch');wrapper.addEventListener('click',this.handleClick.bind(this));shadow.appendChild(this._template);ComponentUtils.style.call(this,baseurl);var dirObserver=new MutationObserver(this.updateInternalDir.bind(this));dirObserver.observe(document.documentElement,{attributeFilter:['dir'],attributes:true});this.updateInternalDir();};proto.updateInternalDir=function(){var internal=this.shadowRoot.firstElementChild;if(document.documentElement.dir==='rtl'){internal.setAttribute('dir','rtl');}else{internal.removeAttribute('dir');}};proto.handleClick=function(e){if(e&&e.target.tagName==='A'){return;}
|
||||
e&&e.preventDefault();e&&e.stopImmediatePropagation();var event=new MouseEvent('click',{view:window,bubbles:true,cancelable:true});this.dispatchEvent(event);if(!event.defaultPrevented){this.checked=!this.checked;}
|
||||
this.dispatchEvent(new CustomEvent('change',{bubbles:true,cancelable:false}));};proto.click=function(){this.handleClick();};Object.defineProperty(proto,'checked',{get:function(){return this._input.checked;},set:function(value){this._input.checked=value;}});Object.defineProperty(proto,'disabled',{get:function(){return this.hasAttribute('disabled');},set:function(value){if(value){this.setAttribute('disabled',true);}else{this.removeAttribute('disabled');}}});Object.defineProperty(proto,'name',{get:function(){return this.getAttribute('name');},set:function(value){this.setAttribute('name',value);}});Object.defineProperty(proto,'type',{get:function(){return'gaia-switch';}});var template=document.createElement('template');template.innerHTML=`<span id="switch">
|
||||
<input type="checkbox">
|
||||
<span><content select="label"></content></span>
|
||||
<div class="details"><content select="details"></content></div>
|
||||
<content select="a"></content>
|
||||
</span>`;return document.registerElement('gaia-switch',{prototype:proto});})(window);
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
gaia-switch {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#switch {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
background: none;
|
||||
}
|
||||
|
||||
#switch span {
|
||||
font-size: 1.9rem;
|
||||
color: var(--label-color);
|
||||
padding: 1rem 0;
|
||||
line-height: 3rem;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
/* Override inheriting nowrap from legacy lists building blocks. */
|
||||
white-space: normal;
|
||||
}
|
||||
#switch span:-moz-dir(ltr) { float: left; }
|
||||
#switch span:-moz-dir(rtl) { float: right; }
|
||||
|
||||
#switch span:not(:empty) {
|
||||
padding-inline-end: 6rem;
|
||||
}
|
||||
|
||||
#switch input {
|
||||
margin: 0;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
offset-inline-start: 0;
|
||||
}
|
||||
|
||||
#switch input:checked ~ span::after {
|
||||
background-position: center bottom;
|
||||
}
|
||||
|
||||
/* ----------------------------------
|
||||
* ON/OFF SWITCHES
|
||||
* ---------------------------------- */
|
||||
|
||||
#switch input ~ span::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
offset-inline-end: 0;
|
||||
top: 2.5rem;
|
||||
width: 6rem;
|
||||
margin: -1.4rem 0 0;
|
||||
height: 2.7rem;
|
||||
border-radius: 1.35rem;
|
||||
overflow: hidden;
|
||||
background-color: #e6e6e6;
|
||||
background-image: url(images/background_off.png);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 9.2rem 2.7rem;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
#switch input ~ span:-moz-dir(ltr)::after { background-position: -3.2rem 0; }
|
||||
#switch input ~ span:-moz-dir(rtl)::after { background-position: 0 0; }
|
||||
|
||||
/* switch: 'ON' state */
|
||||
#switch input:checked ~ span:-moz-dir(ltr)::after {
|
||||
background-image: url(images/background.png);
|
||||
background-position: 0 0;
|
||||
}
|
||||
#switch input:checked ~ span:-moz-dir(rtl)::after {
|
||||
background-image: url(images/background_rtl.png);
|
||||
background-position: -3.2rem 0;
|
||||
}
|
||||
|
||||
/* switch: disabled state */
|
||||
#switch input:disabled ~ span::after {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
#switch input.uninit ~ span::after {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* A <details> slot may be specified in addition the the <label>.
|
||||
* When provided, it appears under the <label> and can provide additional context.
|
||||
*/
|
||||
#switch div.details {
|
||||
clear: both;
|
||||
position: relative;
|
||||
top: -1.5rem;
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding-top: 0.5rem;
|
||||
padding-inline-start: 0;
|
||||
padding-inline-end: 8rem;
|
||||
font-size: 1.4rem;
|
||||
line-height: 1.6rem;
|
||||
color: #505859;
|
||||
font-style: normal;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/**
|
||||
* A <a> link slow may be specified in addition to the <label> and <details>.
|
||||
* When provided, it will appear under the <details> section.
|
||||
*/
|
||||
gaia-switch a {
|
||||
position: relative;
|
||||
top: -0.8rem;
|
||||
font-size: 1.7rem;
|
||||
line-height: 2.4rem;
|
||||
font-weight: 400;
|
||||
color: #35679a;
|
||||
}
|
||||
14
assets/webapps/ftu.gaiamobile.org/shared/js/async_storage.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
;;this.asyncStorage=(function(){'use strict';var DBNAME='asyncStorage';var DBVERSION=1;var STORENAME='keyvaluepairs';var db=null;function withDatabase(f){if(db){f();}else{var openreq=indexedDB.open(DBNAME,DBVERSION);openreq.onerror=function withStoreOnError(){console.error('asyncStorage: can\'t open database:',openreq.error.name);};openreq.onupgradeneeded=function withStoreOnUpgradeNeeded(){openreq.result.createObjectStore(STORENAME);};openreq.onsuccess=function withStoreOnSuccess(){db=openreq.result;f();};}}
|
||||
function withStore(type,callback,oncomplete){withDatabase(function(){var transaction=db.transaction(STORENAME,type);if(oncomplete){transaction.oncomplete=oncomplete;}
|
||||
callback(transaction.objectStore(STORENAME));});}
|
||||
function getItem(key,callback){var req;withStore('readonly',function getItemBody(store){req=store.get(key);req.onerror=function getItemOnError(){console.error('Error in asyncStorage.getItem(): ',req.error.name);};},function onComplete(){var value=req.result;if(value===undefined){value=null;}
|
||||
callback(value);});}
|
||||
function setItem(key,value,callback){withStore('readwrite',function setItemBody(store){var req=store.put(value,key);req.onerror=function setItemOnError(){console.error('Error in asyncStorage.setItem(): ',req.error.name);};},callback);}
|
||||
function removeItem(key,callback){withStore('readwrite',function removeItemBody(store){var req=store.delete(key);req.onerror=function removeItemOnError(){console.error('Error in asyncStorage.removeItem(): ',req.error.name);};},callback);}
|
||||
function clear(callback){withStore('readwrite',function clearBody(store){var req=store.clear();req.onerror=function clearOnError(){console.error('Error in asyncStorage.clear(): ',req.error.name);};},callback);}
|
||||
function length(callback){var req;withStore('readonly',function lengthBody(store){req=store.count();req.onerror=function lengthOnError(){console.error('Error in asyncStorage.length(): ',req.error.name);};},function onComplete(){callback(req.result);});}
|
||||
function key(n,callback){if(n<0){callback(null);return;}
|
||||
var req;withStore('readonly',function keyBody(store){var advanced=false;req=store.openCursor();req.onsuccess=function keyOnSuccess(){var cursor=req.result;if(!cursor){return;}
|
||||
if(n===0||advanced){return;}
|
||||
advanced=true;cursor.advance(n);};req.onerror=function keyOnError(){console.error('Error in asyncStorage.key(): ',req.error.name);};},function onComplete(){var cursor=req.result;callback(cursor?cursor.key:null);});}
|
||||
return{getItem:getItem,setItem:setItem,removeItem:removeItem,clear:clear,length:length,key:key};}());
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
;'use strict';var Basket={DATASTORE_NAME:'Basket_Newsletter',basketUrl:'https://basket.mozilla.org/news/subscribe/',newsletterId:'firefox-os',callback:null,xhr:null,errors:{NETWORK_FAILURE:1,INVALID_EMAIL:2,UNKNOWN_EMAIL:3,UNKNOWN_TOKEN:4,USAGE_ERROR:5,EMAIL_PROVIDER_AUTH_FAILURE:6,AUTH_ERROR:7,SSL_REQUIRED:8,INVALID_NEWSLETTER:9,INVALID_LANGUAGE:10,EMAIL_NOT_CHANGED:11,CHANGE_REQUEST_NOT_FOUND:12,UNKNOWN_ERROR:99},responseHandler:function b_responseHandler(){if(this.xhr.readyState===4){if(this.xhr.status===200){if(typeof this.callback==='function'){this.callback(null,JSON.parse(this.xhr.responseText));}}else{if(typeof this.callback==='function'){if(this.xhr.responseText){this.callback(JSON.parse(this.xhr.responseText));}else{this.callback('Unknown error');}}}}},timeoutHandler:function b_timeoutHandler(){this.callback('Timeout');},send:function b_send(email,callback){var self=this;var params='email='+encodeURIComponent(email)+'&newsletters='+self.newsletterId;self.callback=callback;self.xhr=new XMLHttpRequest({mozSystem:true});self.xhr.onreadystatechange=self.responseHandler.bind(self);self.xhr.open('POST',self.basketUrl,true);self.xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded');self.xhr.setRequestHeader('Connection','close');self.xhr.timeout=5000;self.xhr.ontimeout=self.timeoutHandler.bind(self);self.getLanguage(function do_send(language){if(language){params+='&lang='+language;}
|
||||
self.xhr.setRequestHeader('Content-length',params.length);self.xhr.send(params);});},getDataStore:function(){var self=this;return new Promise(function(resolve,reject){navigator.getDataStores(self.DATASTORE_NAME).then(function(stores){if(!stores.length){reject(new Error('DataStore not loaded'));}else{var dataStore=stores[0];if(dataStore.owner==='app://ftu.gaiamobile.org/manifest.webapp'){resolve(dataStore);}else{reject(new Error('Wrong DataStore'));}}});});},store:function b_store(email,callback){this.getDataStore().then(function loaded(store){store.add({'newsletter_email':email}).then(function(id){if(callback){callback(false,id);}});}).catch(function error(err){console.error('Something went wrong: '+err);callback(err);});},getLanguage:function b_getLanguage(callback){var settings=window.navigator.mozSettings;if(!settings||!settings.createLock){callback();return;}
|
||||
var s_name='language.current';var req=settings.createLock().get(s_name);req.onsuccess=function _onsuccess(){callback(req.result[s_name]);};req.onerror=function _onerror(){console.error('Error getting setting: '+s_name);callback();};}};
|
||||
124
assets/webapps/ftu.gaiamobile.org/shared/js/binary_search.js
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
'use strict';
|
||||
/* exported utils */
|
||||
var utils = window.utils || {};
|
||||
|
||||
/**
|
||||
* This function performs a binary search over an already sorted array
|
||||
* target is the target item to search for
|
||||
* array is the sorted array
|
||||
* options is an optional object which may contain
|
||||
* the start and end position (from, to)
|
||||
* an optional arrayField which indicates the object property that contains the
|
||||
* comparable item and transform and compare functions
|
||||
*
|
||||
* Returns an array with the positions on which the target item was found
|
||||
*
|
||||
*/
|
||||
utils.binarySearch = function(target, array, options) {
|
||||
var arrayField = options.arrayField,
|
||||
transformFunction = options.transformFunction,
|
||||
compareFunction = options.compareFunction;
|
||||
|
||||
// Obtains the comparable item by transforming if necessary
|
||||
function getItem(array, index) {
|
||||
var item = array[index];
|
||||
if (arrayField) {
|
||||
item = item[arrayField];
|
||||
if (typeof transformFunction === 'function') {
|
||||
item = transformFunction(item);
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
// Compares the target with an array item
|
||||
function compare(target, item) {
|
||||
var out;
|
||||
if (typeof compareFunction === 'function') {
|
||||
out = compareFunction(target, item);
|
||||
}
|
||||
else {
|
||||
if (typeof target === 'string') {
|
||||
out = target.localeCompare(item);
|
||||
}
|
||||
else {
|
||||
out = target.toString().localeCompare(item);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
var from = options.from;
|
||||
if (typeof from === 'undefined') {
|
||||
from = 0;
|
||||
}
|
||||
var to = options.to;
|
||||
if (typeof to === 'undefined') {
|
||||
to = array.length - 1;
|
||||
}
|
||||
|
||||
if (to < from) {
|
||||
// Not found
|
||||
return [];
|
||||
}
|
||||
|
||||
var middleIndex = Math.floor((to - from) / 2);
|
||||
var item = getItem(array, from + middleIndex);
|
||||
|
||||
var compareResult = compare(target, item);
|
||||
|
||||
if (compareResult === 0) {
|
||||
// Once a result is found let's iterate in both directions to get the rest
|
||||
// Just in case there are more than one result
|
||||
var results = [from + middleIndex];
|
||||
|
||||
var next = from + middleIndex + 1;
|
||||
var finish = false;
|
||||
while (next <= (array.length - 1) && !finish) {
|
||||
item = getItem(array, next);
|
||||
|
||||
if (compare(target, item) === 0) {
|
||||
results.push(next);
|
||||
}
|
||||
else {
|
||||
finish = true;
|
||||
}
|
||||
next++;
|
||||
}
|
||||
|
||||
finish = false;
|
||||
next = from + middleIndex - 1;
|
||||
|
||||
while (next >= 0 && !finish) {
|
||||
item = getItem(array, next);
|
||||
|
||||
if (compare(target, item) === 0) {
|
||||
results.push(next);
|
||||
}
|
||||
else {
|
||||
finish = true;
|
||||
}
|
||||
next--;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
else if (compareResult < 0) {
|
||||
return utils.binarySearch(target, array, {
|
||||
from: from,
|
||||
to: to - middleIndex - 1,
|
||||
arrayField: arrayField,
|
||||
transformFunction: transformFunction,
|
||||
compareFunction: compareFunction
|
||||
});
|
||||
}
|
||||
else {
|
||||
return utils.binarySearch(target, array, {
|
||||
from: from + middleIndex + 1,
|
||||
to: to,
|
||||
arrayField: arrayField,
|
||||
transformFunction: transformFunction,
|
||||
compareFunction: compareFunction
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
;;(function(exports){'use strict';exports.ComponentUtils={style:function(baseUrl){if(window.VulpesCompat){window.VulpesCompat.componentStyle(this,baseUrl).catch(console.error);return;}var style=document.createElement('style');var url=baseUrl+'style.css';var self=this;style.setAttribute('scoped','');style.innerHTML='@import url('+url+');';this.appendChild(style);this.style.visibility='hidden';style.addEventListener('load',function(){if(self.shadowRoot){self.shadowRoot.appendChild(style.cloneNode(true));}
|
||||
self.style.visibility='';});}};}(window));
|
||||
21
assets/webapps/ftu.gaiamobile.org/shared/js/confirm.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
;'use strict';var ConfirmDialog=(function(){var isAnimationPlaying=false;var isShown=false;var doc=document;try{if(!document.getElementById("confirmation-message"))doc=parent.document;}catch(_){}var screen=doc.getElementById('confirmation-message');var titleNode=screen.querySelector('h1');var messageNode=screen.querySelector('p');var action1Node=screen.querySelector('button:nth-of-type(1)');var action1Callback;var action2Node=screen.querySelector('button:nth-of-type(2)');var action2Callback;var oldzIndex;var callBackAndPreventDefault=function callBackAndPreventDefault(ev){if(ev.target===action1Node){action1Callback();ev.preventDefault();return false;}else if(ev.target===action2Node){action2Callback();ev.preventDefault();return false;}};var _hide=function _hide(){if(!isShown){return;}
|
||||
titleNode.removeAttribute('data-l10n-id');titleNode.className='';messageNode.removeAttribute('data-l10n-id');messageNode.className='';action1Node.removeAttribute('data-l10n-id');action1Node.onclick=null;action2Node.removeAttribute('data-l10n-id');action2Node.onclick=null;screen.classList.remove('fade-in');screen.classList.add('fade-out');isAnimationPlaying=true;window.dispatchEvent(new CustomEvent('confirmdialoghiding'));screen.addEventListener('animationend',function cd_fadeOut(ev){action1Node.className='';action2Node.className='';isAnimationPlaying=false;screen.removeEventListener('animationend',cd_fadeOut);screen.classList.add('no-opacity');screen.classList.add('hide');isShown=false;});};var _show=function _show(title,msg,action1,action2,options){var setL10nAttributes=function(element,options){if('string'===typeof options){document.l10n.setAttributes(element,options);}
|
||||
if(options.id){document.l10n.setAttributes(element,options.id,options.args);}};if(options&&options.zIndex){oldzIndex=screen.style.zIndex;screen.style.zIndex=options.zIndex;}
|
||||
else{oldzIndex=null;}
|
||||
if(title){setL10nAttributes(titleNode,title);}else{titleNode.classList.add('hide');}
|
||||
if(msg){setL10nAttributes(messageNode,msg);}else{messageNode.classList.add('hide');}
|
||||
if(action1){if(action1.title){setL10nAttributes(action1Node,action1.title);}
|
||||
if(action1.isDanger){action1Node.classList.add('danger');}
|
||||
if(action1.isRecommend){action1Node.classList.add('recommend');}
|
||||
if(action1.callback){action1Callback=action1.callback;action1Node.onclick=callBackAndPreventDefault;}
|
||||
if(!action2){action2Node.classList.add('hide');action1Node.classList.add('full');}}
|
||||
if(action2){if(action2.title){setL10nAttributes(action2Node,action2.title);}
|
||||
if(action2.isDanger){action2Node.classList.add('danger');}
|
||||
if(action2.isRecommend){action2Node.classList.add('recommend');}
|
||||
if(action2.callback){action2Callback=action2.callback;action2Node.onclick=callBackAndPreventDefault;}
|
||||
if(!action1){action1Node.classList.add('hide');action2Node.classList.add('full');}}
|
||||
if(isShown){return;}
|
||||
screen.classList.remove('hide');screen.classList.remove('fade-out');screen.classList.add('fade-in');isAnimationPlaying=true;window.dispatchEvent(new CustomEvent('confirmdialogshowing'));screen.addEventListener('animationend',function cd_fadeIn(ev){isAnimationPlaying=false;screen.removeEventListener('animationend',cd_fadeIn);screen.classList.remove('no-opacity');isShown=true;});};return{hide:function dialog_hide(){if(oldzIndex){screen.style.zIndex=oldzIndex;oldzIndex=null;}
|
||||
if(!isAnimationPlaying){_hide();return;}
|
||||
screen.addEventListener('animationend',function cd_hideWhenFinished(ev){screen.removeEventListener('animationend',cd_hideWhenFinished);_hide();});},show:function dialog_show(title,msg,action1,action2,options){if(!isAnimationPlaying){_show(title,msg,action1,action2,options);return;}
|
||||
screen.addEventListener('animationend',function cd_showWhenFinished(ev){screen.removeEventListener('animationend',cd_showWhenFinished);_show(title,msg,action1,action2,options);});}};}());
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
;(function(exports){'use strict';function getThumbnail(contact){return getOnePhoto(contact,'begin');}
|
||||
function getFullResolution(contact){return getOnePhoto(contact,'end');}
|
||||
function getOnePhoto(contact,position){if(!contact||!contact.photo||!contact.photo.length){return null;}
|
||||
if(contact.photo.length===1){return contact.photo[0];}
|
||||
var photos=contact.photo;var category=contact.category;if(Array.isArray(category)&&category.indexOf('fb_linked')!==-1){if(photos.length>=4){return photos[(position=='begin')?1:0];}
|
||||
return photos[0];}
|
||||
photos=photosBySize(contact);var index=(position=='begin')?0:photos.length-1;return photos[index];}
|
||||
function photosBySize(contact){var photos=contact.photo.slice(0);photos.sort(function(p1,p2){if(size(p1)<size(p2)){return-1;}
|
||||
if(size(p1)>size(p2)){return 1;}
|
||||
return 0;});return photos;}
|
||||
function size(photo){if(typeof photo=='string'){return photo.length;}
|
||||
return photo.size;}
|
||||
exports.ContactPhotoHelper={getThumbnail:getThumbnail,getFullResolution:getFullResolution};})(window);
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
;'use strict';window.ContactsCleaner=function(contacts){this.lcontacts=contacts;var total=contacts.length;var next=0;var self=this;var CHUNK_SIZE=5;var numResponses=0;var notifyClean=false;var mustHold=false;var holded=false;var mustFinish=false;this.start=function(){mustHold=holded=mustFinish=false;if(total>0){cleanContacts(0);}
|
||||
else if(typeof self.onsuccess==='function'){window.setTimeout(self.onsuccess,0);}};this.hold=function(){mustHold=true;};this.finish=function(){mustFinish=true;if(holded){notifySuccess();}};this.resume=function(){mustHold=holded=mustFinish=false;window.setTimeout(function resume_clean(){cleanContacts(next);});};this.performClean=function(contact,number,cbs){var req=navigator.mozContacts.remove(utils.misc.toMozContact(contact));req.number=number;req.onsuccess=cbs.success;req.onerror=function(e){errorHandler(contact.id,e.target.error);};};function successHandler(e){if(notifyClean||typeof self.oncleaned==='function'){notifyClean=true;var cleaned=e.target.number;window.setTimeout(function(){self.oncleaned(cleaned);},0);}
|
||||
continueCb();}
|
||||
function errorHandler(contactid,error){if(typeof self.onerror==='function'){self.onerror(contactid,error);}
|
||||
continueCb();}
|
||||
function cleanContacts(from){for(var idx=from;idx<(from+CHUNK_SIZE)&&idx<total;idx++){var contact=contacts[idx];self.performClean(contact,idx,{success:successHandler});}}
|
||||
function notifySuccess(){if(typeof self.onsuccess==='function'){window.setTimeout(self.onsuccess);}}
|
||||
function continueCb(){next++;numResponses++;if(next<total&&numResponses===CHUNK_SIZE){numResponses=0;if(!mustHold&&!mustFinish){cleanContacts(next);}
|
||||
else if(mustFinish&&!holded){notifySuccess();}
|
||||
if(mustHold){holded=true;}}
|
||||
else if(next>=total){notifySuccess();}}};
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
;(function(exports){'use strict';function MatcherObj(pdataProvider){var blankRegExp=/\s+/g;var FB_CATEGORY='facebook';var FB_LINKED='fb_linked';var dataProvider=pdataProvider;function MultipleMatcher(ptargets,pmatchingOptions){var next=0;var self=this;var targets=ptargets;var matchingOptions=pmatchingOptions;var finalMatchings={};function doMatchBy(target,callbacks){var options={filterValue:target,filterBy:matchingOptions.filterBy,filterOp:matchingOptions.filterOp};var req=dataProvider.find(options);req.onsuccess=function(){var matchings=req.result.filter(function(aResult){return filterFacebook(aResult,matchingOptions.linkParams);});var filterBy=options.filterBy;var sanitizedTarget=SimplePhoneMatcher.sanitizedNumber(target);var targetVariants=SimplePhoneMatcher.generateVariants(target);matchings.forEach(function(aMatching){if(matchingOptions.selfContactId===aMatching.id){return;}
|
||||
var field=options.filterBy[0];var values=aMatching[field];values.forEach(function(aValue){var value=aValue.value;var sanitizedValue=SimplePhoneMatcher.sanitizedNumber(value);var valueMatched=false;if(targetVariants.length>1){if(targetVariants.indexOf(sanitizedValue)!==-1){valueMatched=true;}}else if(SimplePhoneMatcher.generateVariants(sanitizedValue).indexOf(sanitizedTarget)!==-1){valueMatched=true;}
|
||||
if(valueMatched){var matchings,matchingObj;if(!finalMatchings[aMatching.id]){matchingObj={matchings:{},matchingContact:aMatching};finalMatchings[aMatching.id]=matchingObj;matchingObj.matchings[filterBy[0]]=[];}
|
||||
else{matchingObj=finalMatchings[aMatching.id];}
|
||||
matchings=matchingObj.matchings[filterBy[0]];var sameValueMatchings=matchings.filter(function(matching){return(matching.matchedValue===value);});if(sameValueMatchings.length===0){matchings.push({'target':target,'matchedValue':value});}}});});carryOn();};req.onerror=function(e){window.console.error('Error while trying to do the matching',e.target.error.name);notifyMismatch(self);};}
|
||||
function carryOn(){next++;if(next<targets.length){doMatchBy(targets[next],callbacks);}
|
||||
else if(Object.keys(finalMatchings).length>0){notifyMatch(self,finalMatchings);}
|
||||
else{notifyMismatch(self);}}
|
||||
function matched(contacts){carryOn();}
|
||||
var callbacks={onmatch:matched,onmismatch:carryOn};this.start=function(){doMatchBy(targets[0],callbacks);};}
|
||||
function notifyMatch(obj,matchings){typeof obj.onmatch==='function'&&obj.onmatch(matchings);}
|
||||
function notifyMismatch(obj){typeof obj.onmismatch==='function'&&obj.onmismatch();}
|
||||
function matchBy(aContact,field,filterOper,callbacks,poptions){var options=poptions||{};var values=[];if(Array.isArray(aContact[field])){aContact[field].forEach(function(aField){if(field==='tel'){var variants=SimplePhoneMatcher.generateVariants(aField.value);variants.forEach(function(aVariant){values.push(aVariant);});}
|
||||
else if(typeof aField.value==='string'){values.push(aField.value.trim());}});}
|
||||
if(values.length>0){var matcher=new MultipleMatcher(values,{filterBy:[field],filterOp:filterOper,selfContactId:aContact.id,linkParams:options});matcher.onmatch=callbacks.onmatch;matcher.onmismatch=callbacks.onmismatch;matcher.start();}
|
||||
else{notifyMismatch(callbacks);}}
|
||||
function matchByTel(aContact,callbacks,options){matchBy(aContact,'tel','match',callbacks,options);}
|
||||
function matchByEmail(aContact,callbacks,options){matchBy(aContact,'email','startsWith',callbacks,options);}
|
||||
function doMatch(aContact,mode,callbacks){if(mode==='passive'){doMatchPassive(aContact,callbacks);}
|
||||
else if(mode==='active'){doMatchActive(aContact,callbacks);}}
|
||||
function doMatchTelAndEmail(aContact,callbacks,options){var localCbs={onmatch:function(telMatches){var matchCbs={onmatch:function(mailMatches){var allMatches=telMatches;Object.keys(mailMatches).forEach(function(aMatch){if(!allMatches[aMatch]){allMatches[aMatch]=mailMatches[aMatch];}
|
||||
else{allMatches[aMatch].matchings.email=mailMatches[aMatch].matchings.email;}});notifyMatch(callbacks,allMatches);},onmismatch:function(){notifyMatch(callbacks,telMatches);}};matchByEmail(aContact,matchCbs,options);},onmismatch:function(){matchByEmail(aContact,callbacks,options);}};matchByTel(aContact,localCbs,options);}
|
||||
function getSourceSimUrl(aContact){var out;if(Array.isArray(aContact.category)&&aContact.category.indexOf('sim')!==-1&&Array.isArray(aContact.url)){for(var j=0;j<aContact.url.length;j++){var aUrl=aContact.url[j];if(aUrl.type.indexOf('source')!==-1&&aUrl.type.indexOf('sim')!==-1){out=aUrl.value;}}}
|
||||
return out;}
|
||||
function doMatchPassive(aContact,callbacks){if(!hasName(aContact)){notifyMismatch(callbacks);return;}
|
||||
var simUrl=getSourceSimUrl(aContact);if(simUrl){performPassiveMatchForSimContact(simUrl,aContact,callbacks);}
|
||||
else{performPassiveMatch(aContact,callbacks);}}
|
||||
function performPassiveMatchForSimContact(simUrl,aContact,callbacks){var localCbs={onmatch:function(results){var matchingFound=false;Object.keys(results).forEach(function(aResultId){var matchingContact=results[aResultId].matchingContact;var matchingUrl=getSourceSimUrl(matchingContact);if(matchingUrl&&matchingUrl===simUrl){matchingFound=true;var matchings={};matchings[aResultId]={matchingContact:matchingContact};callbacks.onmatch(matchings);return;}});if(!matchingFound){performPassiveMatch(aContact,callbacks);}},onmismatch:function(){performPassiveMatch(aContact,callbacks);}};matchByName(aContact,localCbs);}
|
||||
function performPassiveMatch(aContact,callbacks){var matchingsFound={};var localCbs={onmatch:function(results){var names=[];Object.keys(results).forEach(function(aResultId){var mContact=results[aResultId].matchingContact;if(!hasName(mContact)){return;}
|
||||
var targetFN=null;if(!isEmptyStr(aContact.familyName)){targetFN=Normalizer.toAscii(aContact.familyName[0].trim().toLowerCase()).replace(blankRegExp,'');}
|
||||
var targetGN=null;if(!isEmptyStr(aContact.givenName)){targetGN=Normalizer.toAscii(aContact.givenName[0].trim().toLowerCase()).replace(blankRegExp,'');}
|
||||
var targetName=(targetGN||'')+(targetFN||'');var mFamilyName=null;var mGivenName=null;if(!isEmptyStr(mContact.familyName)){mFamilyName=Normalizer.toAscii(mContact.familyName[0].trim().toLowerCase()).replace(blankRegExp,'');}
|
||||
if(!isEmptyStr(mContact.givenName)){mGivenName=Normalizer.toAscii(mContact.givenName[0].trim().toLowerCase()).replace(blankRegExp,'');}
|
||||
var mName=(mGivenName||'')+(mFamilyName||'');names.push({contact:mContact,familyName:mFamilyName,givenName:mGivenName,name:mName});var matchingList=names.filter(function(obj){return((obj.familyName===targetFN&&obj.givenName===targetGN)||(obj.name&&obj.name===targetName)&&(!Array.isArray(obj.contact.category)||obj.contact.category.indexOf(FB_CATEGORY)===-1));});matchingList.forEach(function(aMatching){var contact=aMatching.contact;matchingsFound[contact.id]={matchingContact:contact};});});reconcileResults(aContact,matchingsFound,results,callbacks);},onmismatch:function(){notifyMismatch(callbacks);}};doMatchTelAndEmail(aContact,localCbs);}
|
||||
function doMatchActive(aContact,callbacks){var options={linkedMatched:{},linkedTo:getLinkedTo(aContact)};var localCbs={onmatch:function(results){var cbsName={onmatch:function(nameResults){Object.keys(nameResults).forEach(function(aId){if(!results[aId]){results[aId]=nameResults[aId];}
|
||||
else{results[aId].matchings.name=nameResults[aId].matchings.name;}});notifyMatch(callbacks,results);},onmismatch:function(){notifyMatch(callbacks,results);}};matchByName(aContact,cbsName,options);},onmismatch:function(){matchByName(aContact,callbacks,options);}};doMatchTelAndEmail(aContact,localCbs,options);}
|
||||
function notifyFindNameReady(){document.dispatchEvent(new CustomEvent('by_name_ready'));}
|
||||
function matchByName(aContact,callbacks,options){var isSimContact=(Array.isArray(aContact.category)&&aContact.category.indexOf('sim')!==-1);if((isEmptyStr(aContact.familyName)||isEmptyStr(aContact.givenName))&&!isSimContact){notifyMismatch(callbacks);return;}
|
||||
var finalResult={};var resultsByName=[];if(!isEmptyStr(aContact.name)){var targetName=aContact.name[0].trim();var reqName=dataProvider.find({filterValue:targetName,filterBy:['name'],filterOp:'startsWith'});reqName.onsuccess=function(){resultsByName=reqName.result.filter(function(aResult){return filterFacebook(aResult,options);});notifyFindNameReady();};reqName.onerror=function(e){window.console.warn('Error while trying to find by name: ',e.target.error.name);notifyFindNameReady();};}
|
||||
else{notifyFindNameReady();}
|
||||
if(isEmptyStr(aContact.familyName)){endOfMatchByName(finalResult,aContact,resultsByName,callbacks);}
|
||||
else{var targetFamilyName=aContact.familyName[0].trim();var reqFamilyName=dataProvider.find({filterValue:targetFamilyName,filterBy:['familyName'],filterOp:'startsWith'});reqFamilyName.onsuccess=function(){var results=reqFamilyName.result;var givenNames=[];var targetGN=null;if(!isEmptyStr(aContact.givenName)){targetGN=Normalizer.toAscii(aContact.givenName[0].trim().toLowerCase()).replace(blankRegExp,'');}
|
||||
results.forEach(function(mContact){if(mContact.id===aContact.id||isEmptyStr(mContact.givenName)){return;}
|
||||
givenNames.push({contact:mContact,givenName:Normalizer.toAscii(mContact.givenName[0].trim().toLowerCase()).replace(blankRegExp,'')});});var finalMatchings=givenNames.filter(function(obj){var gn=obj.givenName;return((gn===targetGN||targetGN.startsWith(gn)||gn.startsWith(targetGN))&&filterFacebook(obj.contact,options));});finalMatchings.forEach(function(aMatching){finalResult[aMatching.contact.id]={matchings:{'name':[{target:targetFamilyName,matchedValue:Array.isArray(aMatching.contact.name)?aMatching.contact.name[0]:getCompleteName(aMatching.contact)}]},matchingContact:aMatching.contact};});if(resultsByName){endOfMatchByName(finalResult,aContact,resultsByName,callbacks);}
|
||||
else{document.addEventListener('by_name_ready',function nameReady(){document.removeEventListener('by_name_ready',nameReady);endOfMatchByName(finalResult,aContact,resultsByName,callbacks);});}};reqFamilyName.onerror=function(e){window.console.error('Error while trying to find by familyName: ',e.target.error.name);notifyMismatch(callbacks);};}}
|
||||
function getLinkedTo(contact){var out=null;if(Array.isArray(contact.category)){var idx=contact.category.indexOf(FB_LINKED);if(idx!==-1){out=contact.category[idx+1];}}
|
||||
return out;}
|
||||
function isFbLinked(contact){return(Array.isArray(contact.category)&&contact.category.indexOf(FB_LINKED)!==-1);}
|
||||
function isFbContact(contact){return(Array.isArray(contact.category)&&contact.category.indexOf(FB_CATEGORY)!==-1);}
|
||||
function filterFacebook(contact,linkParams){var out=false;if(!isFbContact(contact)){out=true;}
|
||||
else if(isFbLinked(contact)){var linkedTo=getLinkedTo(contact);var targetUid=linkParams.linkedTo||'';var linkedMatched=linkParams.linkedMatched||{};if(targetUid===linkedTo){out=true;}
|
||||
else if((Object.keys(linkedMatched).length===0||linkedMatched[linkedTo])&&!targetUid){linkedMatched[linkedTo]=linkedTo;out=true;}}
|
||||
return out;}
|
||||
function endOfMatchByName(finalResult,aContact,resultsByName,callbacks){resultsByName.forEach(function(aResult){if(aResult.id===aContact.id){return;}
|
||||
var matchingObj={matchings:{'name':[{target:(Array.isArray(aContact.name)&&(typeof aContact.name[0]==='string'))?aContact.name[0].trim():getCompleteName(aContact),matchedValue:Array.isArray(aResult.name)?aResult.name[0]:getCompleteName(aResult)}]},matchingContact:aResult};if(!finalResult[aResult.id]){finalResult[aResult.id]=matchingObj;}
|
||||
else{finalResult[aResult.id].matchings.name=matchingObj.matchings.name;}});if(Object.keys(finalResult).length>0){notifyMatch(callbacks,finalResult);}
|
||||
else{notifyMismatch(callbacks);}}
|
||||
function getCompleteName(contact){var givenName=Array.isArray(contact.givenName)?contact.givenName[0]:'';var familyName=Array.isArray(contact.familyName)?contact.familyName[0]:'';var completeName=givenName&&familyName?givenName+' '+familyName:givenName||familyName;return completeName;}
|
||||
function isEmpty(collection){return((!Array.isArray(collection)||!collection[0])||(collection[0]&&collection[0].value&&!collection[0].value.trim()));}
|
||||
function isEmptyStr(collection){return(!Array.isArray(collection)||(typeof collection[0]!=='string')||!(collection[0].trim()));}
|
||||
function hasName(aContact){return(!isEmptyStr(aContact.givenName)||!isEmptyStr(aContact.familyName));}
|
||||
function reconcileResults(incomingContact,nameMatches,phoneMailMatches,callbacks){var finalMatchings={};Object.keys(nameMatches).forEach(function(aNameMatching){var matchingContact=nameMatches[aNameMatching].matchingContact;var isPhoneMatching=Array.isArray(phoneMailMatches[aNameMatching].matchings.tel);var isMailMatching=Array.isArray(phoneMailMatches[aNameMatching].matchings.email);if(isPhoneMatching&&isMailMatching){finalMatchings[aNameMatching]=phoneMailMatches[aNameMatching];}
|
||||
else if(isPhoneMatching&&(isEmpty(incomingContact.email)||isEmpty(matchingContact.email))){finalMatchings[aNameMatching]=phoneMailMatches[aNameMatching];}
|
||||
else if(isMailMatching&&(isEmpty(incomingContact.tel)||isEmpty(matchingContact.tel))){finalMatchings[aNameMatching]=phoneMailMatches[aNameMatching];}});if(Object.keys(finalMatchings).length>0){notifyMatch(callbacks,finalMatchings);}
|
||||
else{notifyMismatch(callbacks);}}
|
||||
this.match=doMatch;Object.defineProperty(this,'dataProvider',{set:function(theProvider){dataProvider=theProvider;}});}
|
||||
exports.Matcher=new MatcherObj(navigator.mozContacts);}(window));
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
;'use strict';var contacts=window.contacts||{};contacts.Merger=(function(){var DEFAULT_ADR_TYPE='home';var DEFAULT_TEL_TYPE='other';var DEFAULT_EMAIL_TYPE='other';function doMerge(pmasterContact,pmatchingContacts,callbacks){window.setTimeout(function contactsMerge(){mergeAll(pmasterContact,pmatchingContacts,filled,callbacks);},0);}
|
||||
function doInMemoryMerge(pmasterContact,pmatchingContacts){return new Promise(function(resolve,reject){mergeAll(pmasterContact,pmatchingContacts,inMemoryMergeDone,{success:function(masterContact){resolve(masterContact);},error:function(err){reject(err);}});});}
|
||||
function inMemoryMergeDone(callbacks,matchingContacts,masterContact){callbacks.success(masterContact);}
|
||||
function isSimContact(contact){return Array.isArray(contact.category)&&contact.category.indexOf('sim')!==-1;}
|
||||
function mergeAll(masterContact,matchingContacts,onFilled,callbacks){var emailsHash;var categoriesHash;var telsHash;var mergedContact={};var mergedPhoto;mergedContact.givenName=[];copyStringArray(masterContact.givenName,mergedContact.givenName);mergedContact.familyName=[];copyStringArray(masterContact.familyName,mergedContact.familyName);mergedContact.photo=masterContact.photo||[];mergedPhoto=mergedContact.photo;mergedContact.bday=masterContact.bday;mergedContact.anniversary=masterContact.anniversary;mergedContact.adr=masterContact.adr||[];telsHash={};mergedContact.tel=[];if(Array.isArray(masterContact.tel)){masterContact.tel.forEach(function(aTel){if(!telsHash[aTel.value]){aTel.type=(Array.isArray(aTel.type)?aTel.type:[aTel.type]);aTel.type[0]=aTel.type[0]||DEFAULT_TEL_TYPE;mergedContact.tel.push(aTel);}
|
||||
var variants=SimplePhoneMatcher.generateVariants(aTel.value);variants.forEach(function(aVariant){telsHash[aVariant]=true;});});}
|
||||
mergedContact.email=[];emailsHash={};populateEmails(masterContact.email,emailsHash,mergedContact.email);mergedContact.org=masterContact.org||[];mergedContact.category=[];categoriesHash={};populateNoDuplicates(masterContact.category,categoriesHash,mergedContact.category);mergedContact.url=masterContact.url||[];mergedContact.note=masterContact.note||[];matchingContacts.forEach(function(aResult){var theMatchingContact=aResult.matchingContact;var givenName=theMatchingContact.givenName;if(Array.isArray(givenName)&&givenName[0]){if(mergedContact.givenName.indexOf(givenName[0])===-1){if(mergedContact.givenName[0]&&mergedContact.givenName[0].trim()){mergedContact.givenName.push(givenName[0]);}
|
||||
else{mergedContact.givenName[0]=givenName[0];}}}
|
||||
var familyName=theMatchingContact.familyName;if(Array.isArray(familyName)&&familyName[0]){if(mergedContact.familyName.indexOf(familyName[0])===-1){if(mergedContact.familyName[0]&&mergedContact.familyName[0].trim()){mergedContact.familyName.push(familyName[0]);}
|
||||
else{mergedContact.familyName[0]=familyName[0];}}}
|
||||
if((!isDefined(masterContact.givenName)||!isDefined(masterContact.familyName))&&isSimContact(theMatchingContact)){if(isDefined(mergedContact.givenName)&&isDefined(mergedContact.familyName)&&mergedContact.familyName[0]===mergedContact.givenName[0]){if(!isDefined(masterContact.givenName)){mergedContact.givenName=[];mergedContact.name=mergedContact.familyName;}
|
||||
if(!isDefined(masterContact.familyName)){mergedContact.familyName=[];mergedContact.name=mergedContact.givenName;}}}
|
||||
if(!mergedContact.bday&&theMatchingContact.bday){mergedContact.bday=theMatchingContact.bday;}
|
||||
if(!mergedContact.anniversary&&theMatchingContact.anniversary){mergedContact.anniversary=theMatchingContact.anniversary;}
|
||||
if(isDefined(theMatchingContact.org)&&mergedContact.org.length===0){mergedContact.org=theMatchingContact.org;}
|
||||
if(isDefined(theMatchingContact.category)){populateNoDuplicates(theMatchingContact.category,categoriesHash,mergedContact.category);}
|
||||
populateEmails(theMatchingContact.email,emailsHash,mergedContact.email);if(Array.isArray(theMatchingContact.tel)){var theMatchings=aResult.matchings||{};var telMatchings=theMatchings.tel;theMatchingContact.tel.forEach(function(aTel){var theValue=aTel.value;var target=theValue,matchedValue='';if(telMatchings){var matchedFrom=telMatchings.filter(function(x){return(x.target===theValue||x.matchedValue===theValue);});if(matchedFrom[0]){target=matchedFrom[0].target;matchedValue=matchedFrom[0].matchedValue;}}
|
||||
var variants=SimplePhoneMatcher.generateVariants(theValue);var alreadyPresent=false;for(var j=0;j<variants.length;j++){if(telsHash[variants[j]]){alreadyPresent=true;}}
|
||||
if(!alreadyPresent){theValue=target.length>matchedValue.length?target:matchedValue;mergedContact.tel.push({type:aTel.type||[DEFAULT_TEL_TYPE],value:theValue,carrier:aTel.carrier,pref:aTel.pref});variants.forEach(function(aVariant){telsHash[aVariant]=true;});}});}
|
||||
if(!Array.isArray(mergedPhoto)||mergedPhoto.length===0&&isDefined(theMatchingContact.photo)){var photo=ContactPhotoHelper.getFullResolution(theMatchingContact);mergedPhoto=[photo];}
|
||||
populateField(theMatchingContact.adr,mergedContact.adr,DEFAULT_ADR_TYPE);populateField(theMatchingContact.url,mergedContact.url);populateField(theMatchingContact.note,mergedContact.note);});mergedContact.name=[((mergedContact.givenName[0]?mergedContact.givenName[0]:'')+' '+
|
||||
(mergedContact.familyName[0]?mergedContact.familyName[0]:'')).trim()];fillMasterContact(masterContact,mergedContact,mergedPhoto,onFilled.bind(null,callbacks,matchingContacts));}
|
||||
function filled(callbacks,matchingContacts,masterContact){var req=navigator.mozContacts.save(utils.misc.toMozContact(masterContact));req.onsuccess=function(){matchingContacts.forEach(function(aMatchingContact){if(aMatchingContact.matchingContact.id){var contact=aMatchingContact.matchingContact;navigator.mozContacts.remove(utils.misc.toMozContact(contact));}});if(typeof callbacks.success==='function'){callbacks.success(masterContact);}};req.onerror=function(){window.console.error('Error while saving merged Contact: ',req.error.name);typeof callbacks.error==='function'&&callbacks.error(req.error);};}
|
||||
function isDefined(field){return(Array.isArray(field)&&field[0]&&((typeof field[0]==='string'&&field[0].trim().length>0)||typeof field[0]==='object'));}
|
||||
function copyStringArray(source,dest){if(Array.isArray(source)){source.forEach(function(aVal){if(aVal&&aVal.trim()){dest.push(aVal);}});}}
|
||||
function populateEmails(sourceEmails,hash,out){if(Array.isArray(sourceEmails)){sourceEmails.forEach(function(aEmail){aEmail.type=Array.isArray(aEmail.type)?aEmail.type:[aEmail.type];aEmail.type[0]=aEmail.type[0]||DEFAULT_EMAIL_TYPE;var value=aEmail.value;if(value&&value.trim()){value=value.toLowerCase();if(!hash[value]){aEmail.value=value;out.push(aEmail);hash[value]=true;}}});}}
|
||||
function populateNoDuplicates(source,hash,out){if(Array.isArray(source)){source.forEach(function(aCat){if(!hash[aCat]){out.push(aCat);hash[aCat]=true;}});}}
|
||||
function populateField(source,destination,defaultType){if(Array.isArray(source)){var stringifiedDestination=destination.map(function(element){return JSON.stringify(element);});source.forEach(function(as,index){if(stringifiedDestination.indexOf(JSON.stringify(as))===-1){if(defaultType&&(!as.type||!as.type[0])){as.type=[defaultType];}
|
||||
destination.push(as);}});}}
|
||||
function fillMasterContact(masterContact,mergedContact,mergedPhotos,done){var fields=['familyName','givenName','name','org','email','tel','bday','anniversary','adr','category','url','note','photo'];fields.forEach(function(aField){masterContact[aField]=mergedContact[aField];});if(!Array.isArray(mergedPhotos)||!mergedPhotos[0]||mergedPhotos.length>=2){done(masterContact);return;}
|
||||
var mergedPhoto=mergedPhotos[0];utils.thumbnailImage(mergedPhoto,function gotTumbnail(thumbnail){if(mergedPhoto!==thumbnail){masterContact.photo=[mergedPhoto,thumbnail];}else{masterContact.photo=[mergedPhoto];}
|
||||
done(masterContact);});}
|
||||
return{merge:doMerge,inMemoryMerge:doInMemoryMerge};})();
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
;'use strict';var utils=window.utils||{};if(!utils.alphaScroll){(function initScrollbar(doc){var alphaScroll=utils.alphaScroll={};var scrollToCallback,jumper,overlay,overlayStyle,groupSelector,liSearch,liFavorites;var isScrolling=false;var alreadyRendered=false;var isDesktop=false;var P_SCROLLTO_CB='scrollToCb';var P_JUMPER='jumper';var P_OVERLAY='overlay';var P_GROUP_SELECTOR='groupSelector';var TRANSITION_DELAY='0.3s';var TRANSITION_DURATION='0.2s';var RESET_TRANSITION='0s';var offset=0,lastY=0;var isTouch='ontouchstart'in window;var touchstart=isTouch?'touchstart':'mousedown';var touchmove=isTouch?'touchmove':'mousemove';var touchend=isTouch?'touchend':'mouseup';var getY=(function getYWrapper(){return isTouch?function(e){return e.touches[0].pageY;}:function(e){return e.pageY;};})();var getTarget=(function getTargetWrapper(){if(isTouch){return function(e){var touch=e.touches[0];return document.elementFromPoint(touch.pageX,touch.pageY);};}else{return function(e){return e.target;};}})();alphaScroll.init=function(params,desktop){isDesktop=params&¶ms.desktop;if(alreadyRendered&&!isDesktop){return;}
|
||||
scrollToCallback=params[P_SCROLLTO_CB];jumper=params[P_JUMPER];overlay=params[P_OVERLAY];groupSelector=params[P_GROUP_SELECTOR];overlay.textContent='';overlayStyle=overlay.style;jumper.addEventListener(touchstart,scrollStart);jumper.addEventListener(touchmove,scrollTo);jumper.addEventListener(touchend,scrollEnd);if(typeof jumper==='string'){jumper=document.querySelector(jumper);}
|
||||
var template=jumper.querySelector('*[data-template]');if(!template){alreadyRendered=true;return;}
|
||||
var frag=document.createDocumentFragment();var alphabet=[];for(var i=65;i<=90;i++){alphabet.push({anchor:String.fromCharCode(i)});}
|
||||
alphabet.push({anchor:'#'});utils.templates.append(jumper,alphabet,frag);jumper.appendChild(frag);alreadyRendered=true;};alphaScroll.showGroup=function(name){var selector='[data-anchor="group-'+name+'"]';var group=jumper.querySelector(selector);if(group){group.classList.remove('hide');}};alphaScroll.hideGroup=function(name){var selector='[data-anchor="group-'+name+'"]';var group=jumper.querySelector(selector);if(group){group.classList.add('hide');}};function hideExtraItems(value){if(!liSearch){liSearch=jumper.querySelector('li[data-anchor="search-container"]');}
|
||||
if(!liFavorites){liFavorites=jumper.querySelector('li[data-anchor="group-favorites"]');}
|
||||
liSearch.hidden=value;liFavorites.hidden=value;}
|
||||
alphaScroll.toggleFormat=function(type){hideExtraItems(type==='short');};function scrollTo(evt){evt.preventDefault();evt.stopPropagation();if(!isScrolling){return;}
|
||||
var currentY=getY(evt);if(Math.abs(lastY-currentY)<offset/2&&!isDesktop){return;}
|
||||
lastY=currentY;var elem=getTarget(evt);if(!elem){return;}
|
||||
var dataset=elem.dataset;if(dataset.letter){overlay.textContent=dataset.letter;}else if(dataset.img){overlay.textContent='';var img=new Image();img.src='style/images/'+dataset.img;overlay.appendChild(img);}
|
||||
var anch=dataset.anchor;var selector=anch==='group-#'?'group-und':anch;var domTarget=doc.querySelector('#section-'+selector+', #'+selector);if(!domTarget){return;}
|
||||
scrollToCallback(domTarget,selector.replace('group-',''));}
|
||||
function scrollStart(evt){var dataset=getTarget(evt).dataset;evt.preventDefault();evt.stopPropagation();if(!dataset.letter&&!dataset.img){return;}
|
||||
offset=offset||jumper.querySelector('[data-anchor]').offsetHeight;overlayStyle.MozTransitionDelay=RESET_TRANSITION;overlayStyle.MozTransitionDuration=RESET_TRANSITION;overlayStyle.opacity='1';isScrolling=true;scrollTo(evt);}
|
||||
function scrollEnd(evt){evt.preventDefault();evt.stopPropagation();var transitionDelay=TRANSITION_DELAY;var transitionDuration=TRANSITION_DURATION;if(overlay.textContent==='#'||overlay.textContent===''){transitionDelay=RESET_TRANSITION;transitionDuration=RESET_TRANSITION;}
|
||||
overlayStyle.MozTransitionDelay=transitionDelay;overlayStyle.MozTransitionDuration=transitionDuration;overlayStyle.opacity='0';overlay.textContent=null;isScrolling=false;lastY=0;}
|
||||
(function(doc){var images=doc.querySelectorAll('li[data-img]');Object.keys(images).forEach(function(value){var img=new Image();img.src='contacts/style/images/'+images[value].dataset.img;});}(doc));})(document);}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
;'use strict';(function(){var CHUNK_SIZE=5;window.ContactsImporter=function(pContacts,pAccessToken,pConnector){this.contacts=Object.keys(pContacts);var contactsHash=pContacts;var access_token=pAccessToken;var total=this.contacts.length;var self=this;var serviceConnector=pConnector;var numImported=0;var mustHold=false;var holded=false;var mustFinish=false;var isOnLine=navigator.onLine;var numMergedDuplicated=0;window.addEventListener('online',onLineChanged);window.addEventListener('offline',onLineChanged);function onLineChanged(){isOnLine=navigator.onLine;}
|
||||
function contactSaved(e){var cfdata=this;if(typeof self.oncontactimported==='function'){window.setTimeout(function(){self.oncontactimported(cfdata);},0);}
|
||||
continueCb();}
|
||||
function contactSaveError(err){window.console.error('Error while importing contact: ',err.name);if(typeof self.onerror==='function'){window.setTimeout(self.onerror.bind(null,err),0);}
|
||||
continueCb();}
|
||||
function saveMozContact(deviceContact,successCb,errorCb){var req=navigator.mozContacts.save(utils.misc.toMozContact(deviceContact));req.onsuccess=successCb;req.onerror=errorCb;}
|
||||
function pictureReady(blobPicture){var serviceContact=this;var done=function(){var deviceContact=self.adapt(serviceContact);self.persist(deviceContact,contactSaved.bind(serviceContact),contactSaveError);};if(!blobPicture){done();return;}
|
||||
utils.thumbnailImage(blobPicture,function gotThumbnail(thumbnail){if(blobPicture!==thumbnail){serviceContact.photo=[blobPicture,thumbnail];}else{serviceContact.photo=[blobPicture];}
|
||||
done();});}
|
||||
function pictureError(){window.console.error('Error while getting picture for contact: ',this.user_id);self.persist(self.adapt(this),contactSaved.bind(this),contactSaveError);}
|
||||
function pictureTimeout(){window.console.warn('Timeout while getting picture for contact: ',this.user_id);self.persist(self.adapt(this),contactSaved.bind(this),contactSaveError);}
|
||||
this.start=function(){numMergedDuplicated=0;mustHold=false;holded=false;mustFinish=false;importContacts(numImported);};this.hold=function(){mustHold=true;};this.finish=function(){mustFinish=true;if(holded){notifySuccess();}};this.resume=function(){mustHold=false;holded=false;mustFinish=false;window.setTimeout(function resume_import(){importContacts(numImported);},0);};this.persist=function(contactData,successCb,errorCb){var cbs={onmatch:function(matches){numMergedDuplicated++;contacts.adaptAndMerge(this,matches,{success:successCb,error:errorCb});}.bind(utils.misc.toMozContact(contactData)),onmismatch:function(){saveMozContact(this,successCb,function onMismatchError(evt){errorCb(evt.target.error);});}.bind(contactData)};Matcher.match(contactData,'passive',cbs);};this.adapt=function(serviceContact){return serviceConnector.adaptDataForSaving(serviceContact);};function importContacts(from){for(var i=from;i<from+CHUNK_SIZE&&i<total;i++){importContact(i);}}
|
||||
function importContact(index){var serviceContact=contactsHash[self.contacts[index]];var callbacks={success:pictureReady.bind(serviceContact),error:pictureError.bind(serviceContact),timeout:pictureTimeout.bind(serviceContact)};if(isOnLine===true){serviceConnector.downloadContactPicture(serviceContact,access_token,callbacks);}
|
||||
else{callbacks.success(null);}}
|
||||
function notifySuccess(){if(typeof self.onsuccess==='function'){window.setTimeout(function do_success(){self.onsuccess(numImported,numMergedDuplicated);},0);}}
|
||||
function continueCb(){numImported++;var next=numImported+CHUNK_SIZE-1;if(next<total){if(!mustHold&&!mustFinish){importContact(next);}
|
||||
else if(mustFinish&&!holded){notifySuccess();}
|
||||
if(mustHold){holded=true;}}
|
||||
else if(numImported>=total){notifySuccess();}}};})();
|
||||
|
|
@ -0,0 +1,383 @@
|
|||
'use strict';
|
||||
|
||||
/* global fb, utils */
|
||||
/* global Matcher */
|
||||
|
||||
|
||||
if (!window.FacebookConnector) {
|
||||
window.FacebookConnector = (function() {
|
||||
|
||||
var nextUpdateTime;
|
||||
window.fb = window.fb || {};
|
||||
fb.operationsTimeout = parent.config ?
|
||||
parent.config.operationsTimeout : null;
|
||||
|
||||
// Only one instance in order to reuse it for saving friends in the
|
||||
// database. If the instance is not released between transactions, new
|
||||
// objects will be created.
|
||||
var reusedFbContact = new fb.Contact();
|
||||
reusedFbContact.ready = true;
|
||||
|
||||
function createFbImporter(clist, access_token) {
|
||||
/* jshint validthis:true */
|
||||
var out = new window.ContactsImporter(clist, access_token, this);
|
||||
|
||||
out.persist = persistFbData;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
function persistFbData(data, successCb, errorCb, options) {
|
||||
var saveCbs = {
|
||||
success: function(e) {
|
||||
if (options === 'not_match') {
|
||||
successCb();
|
||||
return;
|
||||
}
|
||||
|
||||
var cbs = {
|
||||
onmatch: function(matches) {
|
||||
// For each of the matching contacts link with the just imported
|
||||
Object.keys(matches).forEach(function(aMatchId) {
|
||||
var fbContact = new
|
||||
fb.Contact(matches[aMatchId].matchingContact);
|
||||
|
||||
var mozContReq = fb.utils.getMozContact(data.uid);
|
||||
|
||||
mozContReq.onsuccess = function() {
|
||||
var linkParams = {
|
||||
uid: data.uid,
|
||||
mozContact: mozContReq.result
|
||||
};
|
||||
if (Array.isArray(data.photo) && data.photo[0]) {
|
||||
linkParams.photoUrl = data.pic_big;
|
||||
}
|
||||
var req = fbContact.linkTo(linkParams);
|
||||
|
||||
req.onsuccess = successCb;
|
||||
|
||||
req.onerror = function error(e) {
|
||||
window.console.error('Error while automatically linking : ',
|
||||
aMatchId, data.uid, e.target.name);
|
||||
successCb();
|
||||
};
|
||||
};
|
||||
mozContReq.onerror = function(e) {
|
||||
window.console.error(
|
||||
'Error getting mozContact data in automatic linking: ',
|
||||
e.target.error.name);
|
||||
successCb();
|
||||
};
|
||||
});
|
||||
},
|
||||
onmismatch: successCb
|
||||
};
|
||||
// Try to match and if so merge is performed
|
||||
Matcher.match(data, 'passive', cbs);
|
||||
},
|
||||
error: errorCb
|
||||
};
|
||||
saveFbContact(data, saveCbs.success, saveCbs.error);
|
||||
}
|
||||
|
||||
|
||||
function saveFbContact(data, successCb, errorCb) {
|
||||
var fbContact, successWrapperCb;
|
||||
|
||||
if (reusedFbContact.ready) {
|
||||
reusedFbContact.ready = null;
|
||||
fbContact = reusedFbContact;
|
||||
successWrapperCb = function onsuccess(e) {
|
||||
reusedFbContact.ready = true;
|
||||
successCb(e);
|
||||
};
|
||||
} else {
|
||||
fbContact = new fb.Contact();
|
||||
successWrapperCb = successCb;
|
||||
}
|
||||
|
||||
fbContact.setData(data);
|
||||
|
||||
var req = fbContact.save();
|
||||
req.onsuccess = successWrapperCb;
|
||||
req.onerror = errorCb;
|
||||
}
|
||||
|
||||
function setInfraForSync(existingContacts, friendsImported, callback) {
|
||||
// From the FTU the infra for sync will not be loaded
|
||||
if (!fb.sync) {
|
||||
(typeof callback === 'function') && callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check wether we need to set the update alarm
|
||||
window.asyncStorage.getItem(fb.utils.ALARM_ID_KEY, function(data) {
|
||||
if (!data || (existingContacts && existingContacts.length === 0) &&
|
||||
!friendsImported) {
|
||||
// This is the first contact imported
|
||||
fb.utils.setLastUpdate(nextUpdateTime, function() {
|
||||
var req = fb.sync.scheduleNextSync();
|
||||
if (typeof callback === 'function') {
|
||||
req.onsuccess = callback;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var UID_FILTER_IDX = 5;
|
||||
var acc_tk;
|
||||
|
||||
function buildFriendQuery(uid) {
|
||||
var aquery1 = [].concat(FRIENDS_QUERY);
|
||||
aquery1[UID_FILTER_IDX] = '= ' + uid;
|
||||
|
||||
return aquery1.join('');
|
||||
}
|
||||
|
||||
// Query that retrieves the information about friends
|
||||
var FRIENDS_QUERY = [
|
||||
'SELECT uid, name, first_name, last_name, pic_big, current_location, ' ,
|
||||
'middle_name, birthday_date, email, profile_update_time, ' ,
|
||||
' work, phones, hometown_location' ,
|
||||
' FROM user' ,
|
||||
' WHERE uid ',
|
||||
'IN (SELECT uid1 FROM friend WHERE uid2=me())' ,
|
||||
' ORDER BY ',
|
||||
'first_name'
|
||||
];
|
||||
|
||||
var friendsQueryStr = FRIENDS_QUERY.join('');
|
||||
|
||||
function contactDataLoaded(options, response) {
|
||||
if (!response.error) {
|
||||
// Just in case this is the first contact imported
|
||||
nextUpdateTime = Date.now();
|
||||
/* jshint validthis:true */
|
||||
var successCb = this.success;
|
||||
|
||||
var friend = response.data[0];
|
||||
if (friend) {
|
||||
var out1 = self.adaptDataForShowing(friend);
|
||||
var photoCbs = {};
|
||||
|
||||
photoCbs.success = function(blobPicture) {
|
||||
if (!blobPicture) {
|
||||
pictureLoaded(out1, options, successCb);
|
||||
return;
|
||||
}
|
||||
utils.thumbnailImage(blobPicture,
|
||||
function gotThumbnail(thumbnail) {
|
||||
if (blobPicture !== thumbnail) {
|
||||
out1.photo = [blobPicture, thumbnail];
|
||||
} else {
|
||||
out1.photo = [blobPicture];
|
||||
}
|
||||
pictureLoaded(out1, options, successCb);
|
||||
});
|
||||
}; // successCb
|
||||
|
||||
self.downloadContactPicture(friend, acc_tk, photoCbs);
|
||||
} // if friend
|
||||
else {
|
||||
window.console.error('FB: No Friend data found');
|
||||
/* jshint validthis:true */
|
||||
this.error('No friend data found');
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* jshint validthis:true */
|
||||
this.error(response.error);
|
||||
}
|
||||
}
|
||||
|
||||
function pictureLoaded(friend, options, success) {
|
||||
var data = self.adaptDataForSaving(friend);
|
||||
/* jshint validthis:true */
|
||||
persistFbData(data, function() {
|
||||
success({
|
||||
uid: friend.uid,
|
||||
url: friend.pic_big
|
||||
});
|
||||
}, function error(err) {
|
||||
console.error('Error while persisting: ', err);
|
||||
}, options);
|
||||
|
||||
// If there is no an alarm set it has to be set
|
||||
window.asyncStorage.getItem(fb.utils.ALARM_ID_KEY, function(data) {
|
||||
if (!data) {
|
||||
fb.utils.setLastUpdate(nextUpdateTime,
|
||||
fb.sync.scheduleNextSync);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function FacebookConnector() { }
|
||||
|
||||
FacebookConnector.prototype = {
|
||||
listAllContacts: function(access_token, callbacks, options) {
|
||||
if (options && options.orderBy === 'lastName') {
|
||||
FRIENDS_QUERY[FRIENDS_QUERY.length] = 'last_name';
|
||||
friendsQueryStr = FRIENDS_QUERY.join('');
|
||||
}
|
||||
return fb.utils.runQuery(friendsQueryStr, {
|
||||
success: callbacks.success,
|
||||
error: callbacks.error,
|
||||
timeout: callbacks.timeout
|
||||
},access_token);
|
||||
},
|
||||
|
||||
listDeviceContacts: function(callbacks) {
|
||||
// Dummy implementation for the time being
|
||||
var req = fb.utils.getAllFbContacts();
|
||||
|
||||
req.onsuccess = function successHandler() {
|
||||
callbacks.success(req.result);
|
||||
};
|
||||
req.onerror = callbacks.error;
|
||||
},
|
||||
|
||||
getImporter: function(contactsList, access_token) {
|
||||
return createFbImporter.bind(this)(contactsList, access_token);
|
||||
},
|
||||
|
||||
// Imports a Contact to FB indexedDB private database
|
||||
importContact: function(uid, access_token, callbacks, options) {
|
||||
acc_tk = access_token;
|
||||
var oneFriendQuery = buildFriendQuery(uid);
|
||||
var auxCallbacks = {
|
||||
success: contactDataLoaded.bind(callbacks, options),
|
||||
error: callbacks.error,
|
||||
timeout: callbacks.timeout
|
||||
};
|
||||
fb.utils.runQuery(oneFriendQuery, auxCallbacks, access_token);
|
||||
},
|
||||
|
||||
cleanContacts: function(contactsList, mode, cb) {
|
||||
if (mode === 'update') {
|
||||
var cleaner = new fb.utils.FbContactsCleaner(contactsList, mode);
|
||||
window.setTimeout(cleaner.start, 0);
|
||||
cb(cleaner);
|
||||
}
|
||||
else {
|
||||
// Mode === clear
|
||||
var req = fb.utils.clearFbData();
|
||||
req.onsuccess = function() {
|
||||
cb(req.result);
|
||||
};
|
||||
|
||||
req.onerror = function(e) {
|
||||
window.console.error('Error while starting cleaning: ',
|
||||
e.target.error.name);
|
||||
// Notifying the UI importer to show an error to the user
|
||||
// As we cannot get a cleaner the param is set to null
|
||||
cb(null);
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
adaptDataForShowing: function(source) {
|
||||
|
||||
var box = utils.misc.getPreferredPictureBox();
|
||||
var picWidth = box.width;
|
||||
var picHeight = box.height;
|
||||
|
||||
var out = fb.friend2mozContact(source);
|
||||
out.contactPictureUri = 'https://graph.facebook.com/' +
|
||||
source.uid + '/picture?type=square' +
|
||||
'&width=' + picWidth +
|
||||
'&height=' + picHeight +
|
||||
'&t=' + Date.now();
|
||||
return out;
|
||||
},
|
||||
|
||||
adaptDataForSaving: function fbAdapt(cfdata) {
|
||||
var worksAt = fb.getWorksAt(cfdata);
|
||||
var address = fb.getAddresses(cfdata);
|
||||
|
||||
var birthDate = null;
|
||||
if (cfdata.birthday_date && cfdata.birthday_date.length > 0) {
|
||||
birthDate = fb.getBirthDate(cfdata.birthday_date);
|
||||
}
|
||||
|
||||
var fbInfo = {
|
||||
bday: birthDate,
|
||||
org: [worksAt]
|
||||
};
|
||||
|
||||
if (address) {
|
||||
fbInfo.adr = address;
|
||||
}
|
||||
|
||||
// This is the short telephone number to enable indexing
|
||||
if (cfdata.shortTelephone) {
|
||||
fbInfo.shortTelephone = cfdata.shortTelephone;
|
||||
delete cfdata.shortTelephone;
|
||||
}
|
||||
|
||||
// Check whether we were able to get the photo or not
|
||||
fbInfo.url = [];
|
||||
|
||||
if (cfdata.photo) {
|
||||
fbInfo.photo = cfdata.photo;
|
||||
if (cfdata.pic_big) {
|
||||
// The URL is stored for synchronization purposes
|
||||
fb.setFriendPictureUrl(fbInfo, cfdata.pic_big);
|
||||
}
|
||||
delete cfdata.photo;
|
||||
}
|
||||
// Facebook info is set and then contact is saved
|
||||
cfdata.fbInfo = fbInfo;
|
||||
|
||||
return cfdata;
|
||||
},
|
||||
|
||||
// It would allow to know the UID of a service contact already imported
|
||||
// on the device. That is needed by the generic importer, for live
|
||||
// a dummy implementation as we are currently not supporting updates
|
||||
getContactUid: function(deviceContact) {
|
||||
return fb.getFriendUid(deviceContact);
|
||||
},
|
||||
|
||||
get name() {
|
||||
return 'facebook';
|
||||
},
|
||||
|
||||
get automaticLogout() {
|
||||
return false;
|
||||
},
|
||||
|
||||
downloadContactPicture: function(contact, access_token, callbacks) {
|
||||
return fb.utils.getFriendPicture(contact.uid, callbacks.success,
|
||||
access_token, utils.misc.getPreferredPictureDetail());
|
||||
},
|
||||
|
||||
oncontactsloaded: function(lfriends) {
|
||||
// This is the timestamp for later syncing as it set at the time
|
||||
// when data was ready
|
||||
nextUpdateTime = Date.now();
|
||||
// Now caching the number
|
||||
fb.utils.setCachedNumFriends(lfriends.length);
|
||||
},
|
||||
|
||||
oncontactsimported: function(existingContacts, friendsImported, cb) {
|
||||
setInfraForSync(existingContacts, friendsImported, cb);
|
||||
fb.contacts.flush();
|
||||
},
|
||||
|
||||
startSync: function(existingContacts, myFriendsByUid, syncFinished) {
|
||||
var callbacks = {
|
||||
success: syncFinished
|
||||
};
|
||||
fb.sync.startWithData(existingContacts, myFriendsByUid, callbacks);
|
||||
},
|
||||
|
||||
scheduleNextSync: function() {
|
||||
fb.sync.scheduleNextSync();
|
||||
}
|
||||
};
|
||||
var self = new FacebookConnector();
|
||||
|
||||
return self;
|
||||
})();
|
||||
}
|
||||
|
|
@ -0,0 +1,659 @@
|
|||
'use strict';
|
||||
|
||||
/* global utils */
|
||||
|
||||
var fb = window.fb || {};
|
||||
|
||||
// Encapsulates all the logic to obtain the data for a FB contact
|
||||
fb.Contact = function(deviceContact, cid) {
|
||||
var contactData;
|
||||
var devContact = deviceContact;
|
||||
var contactid = cid;
|
||||
|
||||
function doGetFacebookUid(data) {
|
||||
return fb.getFriendUid(data);
|
||||
}
|
||||
|
||||
function getFacebookUid() {
|
||||
return doGetFacebookUid(deviceContact);
|
||||
}
|
||||
|
||||
function setFacebookUid(value) {
|
||||
doSetFacebookUid(deviceContact, value);
|
||||
}
|
||||
|
||||
function doSetFacebookUid(dcontact, value) {
|
||||
if (!dcontact.category) {
|
||||
dcontact.category = [];
|
||||
}
|
||||
|
||||
if (dcontact.category.indexOf(fb.CATEGORY) === -1) {
|
||||
markAsFb(dcontact);
|
||||
}
|
||||
|
||||
var idx = dcontact.category.indexOf(fb.CATEGORY);
|
||||
|
||||
dcontact.category[idx + 2] = value;
|
||||
}
|
||||
|
||||
function markAsFb(dcontact) {
|
||||
if (!dcontact.category) {
|
||||
dcontact.category = [];
|
||||
}
|
||||
|
||||
if (dcontact.category.indexOf(fb.CATEGORY) === -1) {
|
||||
dcontact.category.push(fb.CATEGORY);
|
||||
dcontact.category.push(fb.NOT_LINKED);
|
||||
}
|
||||
|
||||
return dcontact;
|
||||
}
|
||||
|
||||
// Mark a mozContact (deviceContact) as linked to a FB contact (uid)
|
||||
function markAsLinked(dcontact, uid) {
|
||||
if (!dcontact.category) {
|
||||
dcontact.category = [];
|
||||
}
|
||||
|
||||
if (dcontact.category.indexOf(fb.LINKED) === -1) {
|
||||
dcontact.category.push(fb.CATEGORY);
|
||||
dcontact.category.push(fb.LINKED);
|
||||
dcontact.category.push(uid);
|
||||
}
|
||||
|
||||
return dcontact;
|
||||
}
|
||||
|
||||
function promoteToLinked(dcontact) {
|
||||
var idx = dcontact.category.indexOf(fb.NOT_LINKED);
|
||||
|
||||
if (idx != -1) {
|
||||
dcontact.category[idx] = fb.LINKED;
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the data for an imported FB Contact
|
||||
this.setData = function(data) {
|
||||
contactData = data;
|
||||
};
|
||||
|
||||
Object.defineProperty(this, 'uid', {
|
||||
get: getFacebookUid,
|
||||
set: setFacebookUid,
|
||||
enumerable: true,
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'mozContact', {
|
||||
get: getDevContact
|
||||
});
|
||||
|
||||
function getDevContact() {
|
||||
return devContact;
|
||||
}
|
||||
|
||||
// For saving an imported FB contact
|
||||
this.save = function() {
|
||||
var outReq = new fb.utils.Request();
|
||||
|
||||
if (contactData && navigator.mozContacts) {
|
||||
window.setTimeout(function save_do() {
|
||||
var photoList = contactData.fbInfo.photo;
|
||||
if (photoList && photoList.length > 0 && photoList[0]) {
|
||||
utils.squareImage(photoList[0], function squared(squared_image) {
|
||||
photoList[0] = squared_image;
|
||||
doSave(outReq);
|
||||
});
|
||||
}
|
||||
else {
|
||||
doSave(outReq);
|
||||
}
|
||||
},0);
|
||||
}
|
||||
else {
|
||||
throw 'Data or mozContacts not available';
|
||||
}
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
function doSave(outReq) {
|
||||
// Info to be saved on mozContacts
|
||||
var contactInfo = {};
|
||||
|
||||
// Copying names to the mozContact
|
||||
copyNames(contactData, contactInfo);
|
||||
// URL (photo, etc) is stored also with the Device contact
|
||||
if (contactData.fbInfo.url) {
|
||||
contactInfo.url = contactData.fbInfo.url;
|
||||
}
|
||||
|
||||
doSetFacebookUid(contactInfo, contactData.uid);
|
||||
|
||||
var fbReq = persistToFbCache(contactData);
|
||||
|
||||
fbReq.onsuccess = function() {
|
||||
var mozContactsReq = navigator.mozContacts.save(
|
||||
utils.misc.toMozContact(contactInfo));
|
||||
mozContactsReq.onsuccess = function(e) {
|
||||
outReq.done(fbReq.result);
|
||||
};
|
||||
mozContactsReq.onerror = function(e) {
|
||||
window.console.error('FB: Error while saving on mozContacts',
|
||||
e.target.error);
|
||||
outReq.failed(e.target.error);
|
||||
}; // fbReq.onsuccess
|
||||
};
|
||||
fbReq.onerror = function() {
|
||||
window.console.error('FB: Error while saving on datastore',
|
||||
fbReq.error && fbReq.error.name);
|
||||
outReq.failed(fbReq.error);
|
||||
};
|
||||
}
|
||||
|
||||
// Persists FB Friend Data to the FB cache
|
||||
function persistToFbCache(contactData, mUpdate) {
|
||||
var isUpdate = (mUpdate === true ? mUpdate : false);
|
||||
|
||||
var outReq = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function persist_fb_do() {
|
||||
// now saving the FB-originated data to the "private cache area"
|
||||
var data = Object.create(contactData.fbInfo);
|
||||
|
||||
data.tel = contactData.tel || [];
|
||||
data.email = contactData.email || [];
|
||||
data.uid = contactData.uid;
|
||||
|
||||
Object.keys(contactData.fbInfo).forEach(function(prop) {
|
||||
data[prop] = contactData.fbInfo[prop];
|
||||
});
|
||||
|
||||
// Names are also stored on datastore
|
||||
// thus restoring the contact (if unlinked) will be trivial
|
||||
copyNames(contactData, data);
|
||||
|
||||
var updaterFn = (isUpdate === true ?
|
||||
fb.contacts.update : fb.contacts.save);
|
||||
var fbReq = updaterFn(data);
|
||||
|
||||
fbReq.onsuccess = function() {
|
||||
outReq.done(fbReq.result);
|
||||
};
|
||||
fbReq.onerror = function() {
|
||||
window.console.error('FB: Error while saving Fb data on the Datastore',
|
||||
fbReq.error && fbReq.error.name);
|
||||
outReq.failed(fbReq.error);
|
||||
};
|
||||
},0);
|
||||
|
||||
return outReq;
|
||||
}
|
||||
|
||||
// Updates a FB Contact
|
||||
this.update = function(contactData) {
|
||||
|
||||
// Auxiliary function to do all the work for saving to the FB cache
|
||||
function auxDoUpdate(contactData, outReq) {
|
||||
// If the photo was not updated it is needed to save it
|
||||
if (!contactData.fbInfo.photo) {
|
||||
var dataReq = fb.contacts.get(contactData.uid);
|
||||
dataReq.onsuccess = function() {
|
||||
contactData.fbInfo.photo = dataReq.result.photo;
|
||||
auxCachePersist(contactData, outReq, true);
|
||||
};
|
||||
dataReq.onerror = function() {
|
||||
window.console.error('Error while retrieving existing photo for ',
|
||||
contactData.uid, dataReq.error);
|
||||
outReq.failed(dataReq.error);
|
||||
};
|
||||
}
|
||||
else {
|
||||
utils.squareImage(contactData.fbInfo.photo[0],
|
||||
function sq_img(squaredImg) {
|
||||
contactData.fbInfo.photo[0] = squaredImg;
|
||||
auxCachePersist(contactData, outReq, true);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Persist the data to the FB Cache
|
||||
function auxCachePersist(contactData, outReq, isUpdate) {
|
||||
var fbReq = persistToFbCache(contactData, isUpdate);
|
||||
|
||||
fbReq.onsuccess = function() {
|
||||
outReq.done(fbReq.result);
|
||||
};
|
||||
fbReq.onerror = function() {
|
||||
window.console.error('FB: Error while saving to FB cache: ',
|
||||
contactData.uid, fbReq.error);
|
||||
outReq.failed(fbReq.error);
|
||||
};
|
||||
}
|
||||
|
||||
// Code starts here
|
||||
var outReq = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function update_do() {
|
||||
if (!fb.isFbLinked(devContact)) {
|
||||
copyNames(contactData, devContact);
|
||||
} else {
|
||||
// We are going to update names if they are propagated
|
||||
revisitPropagatedNames(contactData, devContact);
|
||||
}
|
||||
|
||||
// Check whether the photo has changed
|
||||
if (contactData.fbInfo.photo) {
|
||||
devContact.url = contactData.fbInfo.url;
|
||||
}
|
||||
|
||||
var auxReq = new fb.utils.Request();
|
||||
auxReq.onsuccess = function() {
|
||||
var mozContactsReq = navigator.mozContacts.save(
|
||||
utils.misc.toMozContact(devContact));
|
||||
mozContactsReq.onsuccess = function(e) {
|
||||
outReq.done();
|
||||
};
|
||||
|
||||
mozContactsReq.onerror = function(e) {
|
||||
window.console.error('FB: Error while saving mozContact: ',
|
||||
devContact.id, e.target.error);
|
||||
outReq.failed(e.target.error);
|
||||
};
|
||||
}; // auxReq.onsuccess
|
||||
|
||||
auxReq.onerror = function(e) {
|
||||
outReq.failed(e.target.error);
|
||||
}; // auxReq.onerror
|
||||
|
||||
// And now doing the update
|
||||
auxDoUpdate(contactData, auxReq);
|
||||
},0);
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
function asArrayOfValues(value) {
|
||||
return Array.isArray(value) ? value : [value];
|
||||
}
|
||||
|
||||
function copyNames(source, destination) {
|
||||
destination.name = asArrayOfValues(source.name);
|
||||
destination.givenName = asArrayOfValues(source.givenName);
|
||||
destination.familyName = asArrayOfValues(source.familyName);
|
||||
destination.additionalName = asArrayOfValues(source.additionalName);
|
||||
}
|
||||
|
||||
// Merges mozContact data with Facebook data
|
||||
this.merge = function(fbdata) {
|
||||
return fb.mergeContact(devContact, fbdata);
|
||||
};
|
||||
|
||||
|
||||
// Gets the data
|
||||
this.getData = function() {
|
||||
return fb.getData(devContact);
|
||||
};
|
||||
|
||||
this.getDataAndValues = function() {
|
||||
var outReq = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function do_getData() {
|
||||
var uid = doGetFacebookUid(devContact);
|
||||
|
||||
if (uid) {
|
||||
var fbreq = fb.contacts.get(uid);
|
||||
|
||||
fbreq.onsuccess = (function() {
|
||||
var fbdata = fbreq.result;
|
||||
|
||||
var out1 = this.merge(fbdata);
|
||||
|
||||
var out2 = {};
|
||||
|
||||
var duplicates = {};
|
||||
Object.keys(fbdata).forEach(function(key) {
|
||||
var dataElement = fbdata[key];
|
||||
|
||||
if (dataElement && Array.isArray(dataElement) && key !== 'photo') {
|
||||
dataElement.forEach(function(item) {
|
||||
if (item.value && item.value.length > 0) {
|
||||
// Check for duplicates. Those duplicates are annotated to
|
||||
// be later removed from the out2 array
|
||||
var dupList = fb.checkDuplicates(key, item, devContact[key],
|
||||
fbdata.shortTelephone);
|
||||
dupList.forEach(function(aDup) {
|
||||
duplicates[aDup] = true;
|
||||
});
|
||||
|
||||
out2[item.value] = true;
|
||||
}
|
||||
else if (typeof item === 'string' && item.length > 0) {
|
||||
out2[item] = true;
|
||||
}
|
||||
else if (key === 'adr') {
|
||||
if (item.locality) {
|
||||
out2[item.locality] = true;
|
||||
}
|
||||
if (item.countryName) {
|
||||
out2[item.countryName] = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (key === 'photo') {
|
||||
out2.hasPhoto = true;
|
||||
}
|
||||
else if (dataElement) {
|
||||
out2[dataElement] = true;
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(duplicates).forEach(function(aDup) {
|
||||
delete out2[aDup];
|
||||
});
|
||||
|
||||
outReq.done([out1, out2, fbdata]);
|
||||
|
||||
}).bind(this);
|
||||
|
||||
fbreq.onerror = function() {
|
||||
outReq.failed(fbreq.error);
|
||||
};
|
||||
}
|
||||
else {
|
||||
outReq.done([devContact, {}]);
|
||||
}
|
||||
}.bind(this), 0);
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
this.promoteToLinked = function() {
|
||||
promoteToLinked(devContact);
|
||||
};
|
||||
|
||||
this.linkTo = function(fbFriend) {
|
||||
var out = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function do_linkTo() {
|
||||
if (!devContact) {
|
||||
// We need to get the Contact data
|
||||
var req = fb.utils.getContactData(contactid);
|
||||
|
||||
req.onsuccess = function() {
|
||||
devContact = req.result;
|
||||
doLink(devContact, fbFriend, out);
|
||||
}; // req.onsuccess
|
||||
|
||||
req.onerror = function() {
|
||||
throw 'FB: Error while retrieving contact data';
|
||||
};
|
||||
} // devContact
|
||||
else {
|
||||
doLink(devContact, fbFriend, out);
|
||||
}
|
||||
},0);
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
function propagateField(field, from, to) {
|
||||
var copied = false;
|
||||
|
||||
// The field is copied when it is undefined in the target object
|
||||
if (!Array.isArray(to[field]) || !to[field][0] || !to[field][0].trim()) {
|
||||
to[field] = from[field];
|
||||
copied = true;
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
function createName(contact) {
|
||||
contact.name = [];
|
||||
|
||||
if (Array.isArray(contact.givenName)) {
|
||||
contact.name[0] = contact.givenName[0] + ' ';
|
||||
}
|
||||
|
||||
if (Array.isArray(contact.familyName)) {
|
||||
contact.name[0] += contact.familyName[0];
|
||||
}
|
||||
}
|
||||
|
||||
function propagateNames(from, to) {
|
||||
var isGivenNamePropagated = propagateField('givenName', from, to);
|
||||
var isFamilyNamePropagated = propagateField('familyName', from, to);
|
||||
|
||||
if (isGivenNamePropagated || isFamilyNamePropagated) {
|
||||
// We are going to mark the propagation in the category field
|
||||
if (isGivenNamePropagated) {
|
||||
fb.setPropagatedFlag('givenName', to);
|
||||
}
|
||||
|
||||
if (isFamilyNamePropagated) {
|
||||
fb.setPropagatedFlag('familyName', to);
|
||||
}
|
||||
|
||||
createName(to);
|
||||
}
|
||||
}
|
||||
|
||||
// This method copies names to a contact when they are propagated from fb
|
||||
function revisitPropagatedNames(from, to) {
|
||||
if (fb.isPropagated('givenName', to)) {
|
||||
to.givenName = from.givenName;
|
||||
}
|
||||
|
||||
if (fb.isPropagated('familyName', to)) {
|
||||
to.familyName = from.familyName;
|
||||
}
|
||||
|
||||
createName(to);
|
||||
}
|
||||
|
||||
function doLink(contactdata, fbFriend, out) {
|
||||
if (contactdata) {
|
||||
if (fbFriend.uid) {
|
||||
// When marking as linked is needed to store a reference to the profile
|
||||
// picture URL
|
||||
markAsLinked(contactdata, fbFriend.uid);
|
||||
if (fbFriend.photoUrl) {
|
||||
fb.setFriendPictureUrl(contactdata, fbFriend.photoUrl);
|
||||
}
|
||||
else if (fbFriend.mozContact) {
|
||||
contactdata.url = fbFriend.mozContact.url;
|
||||
}
|
||||
}
|
||||
else if (fbFriend.mozContact) {
|
||||
markAsLinked(contactdata, doGetFacebookUid(fbFriend.mozContact));
|
||||
contactdata.url = fbFriend.mozContact.url;
|
||||
}
|
||||
|
||||
propagateNames(fbFriend.mozContact, contactdata);
|
||||
var mozContactsReq = navigator.mozContacts.save(
|
||||
utils.misc.toMozContact(contactdata));
|
||||
|
||||
mozContactsReq.onsuccess = function(e) {
|
||||
// The FB contact on mozContacts needs to be removed
|
||||
if (fbFriend.mozContact && !fb.isFbLinked(fbFriend.mozContact)) {
|
||||
var deleteReq = navigator.mozContacts.remove(
|
||||
utils.misc.toMozContact(fbFriend.mozContact));
|
||||
|
||||
deleteReq.onsuccess = function(e) {
|
||||
out.done(e.target.result);
|
||||
};
|
||||
|
||||
deleteReq.onerror = function(e) {
|
||||
window.console.error('FB: Error while linking');
|
||||
out.failed(e.target.error);
|
||||
};
|
||||
}
|
||||
else {
|
||||
out.done(e.target.result);
|
||||
}
|
||||
|
||||
}; // mozContactsReq.onsuccess
|
||||
|
||||
mozContactsReq.onerror = function(e) {
|
||||
out.failed(e.target.error);
|
||||
}; // mozContactsReq.onerror
|
||||
} // if(dev.contact)
|
||||
else {
|
||||
throw 'FB: Contact data not defined';
|
||||
}
|
||||
}
|
||||
|
||||
// if type === 'hard' the FB Friend is removed from the cache
|
||||
this.unlink = function(type) {
|
||||
var out = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function do_unlink() {
|
||||
if (!devContact) {
|
||||
// We need to get the Contact data
|
||||
var req = fb.utils.getContactData(contactid);
|
||||
|
||||
req.onsuccess = function() {
|
||||
devContact = req.result;
|
||||
doUnlink(devContact, out, type);
|
||||
}; // req.onsuccess
|
||||
|
||||
req.onerror = function() {
|
||||
throw 'FB: Error while retrieving contact data';
|
||||
};
|
||||
} // devContact
|
||||
else {
|
||||
doUnlink(devContact, out, type);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
// Reset givenName and familyName if it is needed after unlinking
|
||||
function resetNames(dContact) {
|
||||
fb.resetNames(dContact);
|
||||
}
|
||||
|
||||
function doUnlink(dContact, out, type) {
|
||||
var theType = type || 'soft';
|
||||
var uid = doGetFacebookUid(dContact);
|
||||
|
||||
resetNames(dContact);
|
||||
fb.markAsUnlinked(dContact);
|
||||
var req = navigator.mozContacts.save(utils.misc.toMozContact(dContact));
|
||||
|
||||
req.onsuccess = function(e) {
|
||||
if (theType !== 'hard') {
|
||||
|
||||
// Then the original FB imported contact is restored
|
||||
var fbNumReq = fb.utils.getNumberMozContacts(uid);
|
||||
|
||||
fbNumReq.onsuccess = function() {
|
||||
// Because we have already unlinked one
|
||||
if (fbNumReq.result >= 1) {
|
||||
out.done();
|
||||
} else {
|
||||
var fbDataReq = fb.contacts.get(uid);
|
||||
|
||||
fbDataReq.onsuccess = function() {
|
||||
var imported = fbDataReq.result;
|
||||
|
||||
var data = {};
|
||||
copyNames(imported, data);
|
||||
data.url = imported.url;
|
||||
doSetFacebookUid(data, uid);
|
||||
|
||||
// The FB contact is restored
|
||||
var aMozContact = utils.misc.toMozContact(data);
|
||||
var reqRestore = navigator.mozContacts.save(aMozContact);
|
||||
|
||||
reqRestore.onsuccess = function(e) {
|
||||
out.done(aMozContact.id);
|
||||
};
|
||||
|
||||
reqRestore.onerror = function(e) {
|
||||
out.failed(e.target.error);
|
||||
};
|
||||
};
|
||||
|
||||
fbDataReq.onerror = function() {
|
||||
window.console.error('FB: Error while unlinking contact data');
|
||||
out.failed(fbDataReq.error);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
fbNumReq.onerror = function() {
|
||||
window.console.error('FB: Error while unlinking contact data');
|
||||
out.failed(fbNumReq.error);
|
||||
};
|
||||
}
|
||||
else {
|
||||
// FB Data is removed from the cache
|
||||
var removeReq = fb.contacts.remove(uid, true);
|
||||
|
||||
removeReq.onsuccess = function() {
|
||||
out.done(removeReq.result);
|
||||
};
|
||||
|
||||
removeReq.onerror = function() {
|
||||
out.failed(removeReq.error);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
req.onerror = function(e) {
|
||||
out.failed(e.target.error);
|
||||
};
|
||||
}
|
||||
|
||||
this.remove = function(pforceFlush) {
|
||||
var forceFlush = (pforceFlush === true) ? pforceFlush : false;
|
||||
|
||||
var out = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function do_remove() {
|
||||
var uid = doGetFacebookUid(devContact);
|
||||
|
||||
var fbNumReq = fb.utils.getNumberMozContacts(uid);
|
||||
fbNumReq.onsuccess = function num_onsuccess() {
|
||||
var removeReq;
|
||||
// If there is only one Device Contact associated
|
||||
// Then corresponding FB Data is removed otherwise only
|
||||
// the device contact is removed
|
||||
if (fbNumReq.result === 1) {
|
||||
removeReq = navigator.mozContacts.remove(
|
||||
utils.misc.toMozContact(devContact));
|
||||
removeReq.onsuccess = function(e) {
|
||||
var fbReq = fb.contacts.remove(uid, forceFlush);
|
||||
fbReq.onsuccess = function() {
|
||||
out.done(fbReq.result);
|
||||
};
|
||||
|
||||
fbReq.onerror = function() {
|
||||
out.failed(fbReq.error);
|
||||
};
|
||||
};
|
||||
removeReq.onerror = function(e) {
|
||||
out.failed(e.target.error);
|
||||
};
|
||||
}
|
||||
else {
|
||||
removeReq = navigator.mozContacts.remove(
|
||||
utils.misc.toMozContact(devContact));
|
||||
removeReq.onsuccess = function(e) {
|
||||
out.done();
|
||||
};
|
||||
removeReq.onerror = function(e) {
|
||||
out.failed(e.target.error);
|
||||
};
|
||||
}
|
||||
};
|
||||
}, 0);
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
}; // fb.Contact
|
||||
|
|
@ -0,0 +1,260 @@
|
|||
'use strict';
|
||||
|
||||
/* global fb, utils */
|
||||
|
||||
/* jshint ignore:start */
|
||||
var fb = this.fb || {};
|
||||
/* jshint ignore:end */
|
||||
|
||||
fb.PROPAGATED_PREFIX = 'fb_propagated_';
|
||||
|
||||
// Types of URLs for FB Information
|
||||
fb.PROFILE_PHOTO_URI = 'fb_profile_photo';
|
||||
fb.FRIEND_URI = 'fb_friend';
|
||||
fb.DEFAULT_PHONE_TYPE = 'other';
|
||||
fb.DEFAULT_EMAIL_TYPE = 'other';
|
||||
// This year indicates that the year can be ignored (yearless date)
|
||||
fb.FLAG_YEAR_IGNORED = 9996;
|
||||
|
||||
fb.CONTACTS_APP_ORIGIN = location.origin;
|
||||
|
||||
fb.isPropagated = function fcu_isPropagated(field, devContact) {
|
||||
return (devContact.category && devContact.category.
|
||||
indexOf(fb.PROPAGATED_PREFIX + field) !== -1);
|
||||
};
|
||||
|
||||
fb.removePropagatedFlag = function fcu_removePropagatedFlag(field, devContact) {
|
||||
var idx = devContact.category.indexOf(fb.PROPAGATED_PREFIX + field);
|
||||
if (idx !== -1) {
|
||||
devContact.category.splice(idx, 1);
|
||||
}
|
||||
};
|
||||
|
||||
fb.setPropagatedFlag = function fcu_setPropagatedFlag(field, devContact) {
|
||||
var idx = devContact.category.indexOf(fb.PROPAGATED_PREFIX + field);
|
||||
if (idx === -1) {
|
||||
devContact.category.push(fb.PROPAGATED_PREFIX + field);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
fb.getFriendPictureUrl = function(devContact) {
|
||||
var out;
|
||||
|
||||
var urls = devContact.url;
|
||||
|
||||
if (urls) {
|
||||
for (var c = 0; c < urls.length; c++) {
|
||||
var aurl = urls[c];
|
||||
if (aurl.type.indexOf(fb.PROFILE_PHOTO_URI) !== -1) {
|
||||
out = aurl.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
fb.setFriendPictureUrl = function(devContact, url) {
|
||||
var urls = devContact.url || [];
|
||||
|
||||
urls.push({
|
||||
type: [fb.PROFILE_PHOTO_URI],
|
||||
value: url
|
||||
});
|
||||
|
||||
devContact.url = urls;
|
||||
};
|
||||
|
||||
// Adapts data to the mozContact format names
|
||||
fb.friend2mozContact = function(f) {
|
||||
|
||||
function normalizeFbPhoneNumber(phone) {
|
||||
var out = phone.number;
|
||||
if (phone.country_code && out.indexOf('+') !== 0) {
|
||||
out = '+' + phone.country_code + out;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Check whether this has been already normalized to mozContact
|
||||
if (Array.isArray(f.familyName)) {
|
||||
return f;
|
||||
}
|
||||
|
||||
// givenName is put as name but it should be f.first_name
|
||||
f.familyName = [f.last_name ? f.last_name.trim() : (f.last_name || '')];
|
||||
var middleName = f.middle_name ? f.middle_name.trim() : (f.middle_name || '');
|
||||
f.additionalName = middleName;
|
||||
var firstName = f.first_name ? f.first_name.trim() : (f.first_name || '');
|
||||
f.givenName = [(firstName + ' ' + middleName).trim()];
|
||||
|
||||
delete f.last_name;
|
||||
delete f.middle_name;
|
||||
delete f.first_name;
|
||||
|
||||
if (f.email) {
|
||||
f.email1 = f.email;
|
||||
f.email = [{
|
||||
type: [fb.DEFAULT_EMAIL_TYPE],
|
||||
value: f.email
|
||||
}];
|
||||
}
|
||||
else {
|
||||
f.email1 = '';
|
||||
}
|
||||
|
||||
if (Array.isArray(f.phones) && f.phones.length > 0) {
|
||||
f.tel = [];
|
||||
f.shortTelephone = [];
|
||||
f.phones.forEach(function(aphone) {
|
||||
f.tel.push({
|
||||
type: [fb.DEFAULT_PHONE_TYPE],
|
||||
value: normalizeFbPhoneNumber(aphone)
|
||||
});
|
||||
// Enabling to find FB phones by short number
|
||||
f.shortTelephone.push(aphone.number);
|
||||
});
|
||||
}
|
||||
|
||||
delete f.phones;
|
||||
|
||||
f.uid = f.uid.toString();
|
||||
|
||||
return f;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Auxiliary function to know where a contact works
|
||||
*
|
||||
*/
|
||||
fb.getWorksAt = function(fbdata) {
|
||||
var out = '';
|
||||
if (fbdata.work && fbdata.work.length > 0) {
|
||||
// It is assumed that first is the latest
|
||||
out = fbdata.work[0].employer.name;
|
||||
}
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
/**
|
||||
* Facebook dates are MM/DD/YYYY
|
||||
*
|
||||
* Returns the birth date
|
||||
*
|
||||
*/
|
||||
fb.getBirthDate = function getBirthDate(sbday) {
|
||||
var out = new Date(0);
|
||||
|
||||
var imonth = sbday.indexOf('/');
|
||||
var smonth = sbday.substring(0, imonth);
|
||||
|
||||
var iyear = sbday.lastIndexOf('/');
|
||||
if (iyear === imonth) {
|
||||
iyear = sbday.length;
|
||||
}
|
||||
var sday = sbday.substring(imonth + 1, iyear);
|
||||
|
||||
var syear = sbday.substring(iyear + 1, sbday.length);
|
||||
|
||||
out.setUTCDate(parseInt(sday, 10));
|
||||
out.setUTCMonth(parseInt(smonth, 10) - 1, parseInt(sday, 10));
|
||||
|
||||
if (syear && syear.length > 0) {
|
||||
out.setUTCFullYear(parseInt(syear, 10));
|
||||
}
|
||||
else {
|
||||
// 9996 is the year that flags a not known year
|
||||
out.setUTCFullYear(fb.FLAG_YEAR_IGNORED);
|
||||
}
|
||||
|
||||
out.setUTCHours(0);
|
||||
out.setUTCMinutes(0);
|
||||
out.setUTCSeconds(0);
|
||||
out.setUTCMilliseconds(0);
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
fb.getAddresses = function(fbdata) {
|
||||
|
||||
function fillAddress(fbAddress, type) {
|
||||
var outAddr = {};
|
||||
|
||||
outAddr.type = [type];
|
||||
outAddr.locality = fbAddress.city || '';
|
||||
outAddr.region = fbAddress.state || '';
|
||||
outAddr.countryName = fbAddress.country || '';
|
||||
|
||||
return outAddr;
|
||||
}
|
||||
|
||||
var out = [];
|
||||
var addrTypes = {
|
||||
'home': 'hometown_location',
|
||||
'current': 'current_location'
|
||||
};
|
||||
|
||||
Object.keys(addrTypes).forEach(function onAddressType(type) {
|
||||
var addrObj = fbdata[addrTypes[type]];
|
||||
if (addrObj) {
|
||||
out.push(fillAddress(addrObj, type));
|
||||
}
|
||||
});
|
||||
return out;
|
||||
};
|
||||
|
||||
// The contact is now totally unlinked
|
||||
// [...,facebook, fb_not_linked, 123456,....]
|
||||
fb.markAsUnlinked = function(devContact) {
|
||||
var category = devContact.category;
|
||||
var updatedCategory = [];
|
||||
|
||||
if (category) {
|
||||
var idx = category.indexOf(fb.CATEGORY);
|
||||
if (idx !== -1) {
|
||||
for (var c = 0; c < idx; c++) {
|
||||
updatedCategory.push(category[c]);
|
||||
}
|
||||
// The facebook category, the linked mark and the UID are skipped
|
||||
for (c = idx + 3; c < category.length; c++) {
|
||||
updatedCategory.push(category[c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
devContact.category = updatedCategory;
|
||||
|
||||
return devContact;
|
||||
};
|
||||
|
||||
// Unlinks a FB Contact. This must only be called from the clean all process
|
||||
fb.unlinkClearAll = function(devContact) {
|
||||
fb.resetNames(devContact);
|
||||
fb.markAsUnlinked(devContact);
|
||||
};
|
||||
|
||||
// Reset givenName and familyName if it is needed after unlinking
|
||||
fb.resetNames = function resetNames(dContact) {
|
||||
if (fb.isPropagated('givenName', dContact)) {
|
||||
dContact.givenName = [''];
|
||||
fb.removePropagatedFlag('givenName', dContact);
|
||||
}
|
||||
|
||||
if (fb.isPropagated('familyName', dContact)) {
|
||||
dContact.familyName = [''];
|
||||
fb.removePropagatedFlag('familyName', dContact);
|
||||
}
|
||||
|
||||
dContact.name = [dContact.givenName[0] + ' ' + dContact.familyName[0]];
|
||||
};
|
||||
|
||||
// Marks that FB Cleaning is in progress or not
|
||||
fb.markFbCleaningInProgress = function(value) {
|
||||
utils.cookie.update({
|
||||
fbCleaningInProgress: value
|
||||
});
|
||||
};
|
||||
|
|
@ -0,0 +1,380 @@
|
|||
'use strict';
|
||||
|
||||
/* global LazyLoader, TelIndexer, SimplePhoneMatcher */
|
||||
|
||||
// WARNING: This file lazy loads:
|
||||
// 'shared/js/fb/fb_data_reader.js
|
||||
// 'shared/js/fb/fb_tel_index.js'
|
||||
// 'shared/js/binary_search.js'
|
||||
// 'shared/js/simple_phone_matcher.js' (will be removed once bug 938265 lands)
|
||||
|
||||
var fb = window.fb || {};
|
||||
|
||||
(function() {
|
||||
var contacts = fb.contacts || {};
|
||||
fb.contacts = contacts;
|
||||
|
||||
var Reader;
|
||||
var readerLoaded = false;
|
||||
|
||||
// Record Id for the index
|
||||
var INDEX_ID = 1;
|
||||
var isIndexDirty = false;
|
||||
var READER_LOADED_EV = 'reader_loaded';
|
||||
|
||||
var TEL_INDEXER_JS = '/shared/js/fb/fb_tel_index.js';
|
||||
var PHONE_MATCHER_JS = '/shared/js/simple_phone_matcher.js';
|
||||
var FB_READER_JS = '/shared/js/fb/fb_data_reader.js';
|
||||
var BINARY_SEARCH_JS = '/shared/js/binary_search.js';
|
||||
|
||||
contacts.UID_NOT_FOUND = 'UIDNotFound';
|
||||
contacts.ALREADY_EXISTS = 'AlreadyExists';
|
||||
|
||||
// This is needed for having proxy methods setted and ready before
|
||||
// the real reader methods (in fb_data_reader) are loaded
|
||||
if (!contacts.init) {
|
||||
var proxyMethods = ['get', 'getLength', 'getByPhone', 'search',
|
||||
'refresh', 'init', 'restart'];
|
||||
proxyMethods.forEach(function(aMethod) {
|
||||
contacts[aMethod] = defaultFunction.bind(null, aMethod);
|
||||
});
|
||||
LazyLoader.load(FB_READER_JS, onreaderLoaded);
|
||||
}
|
||||
else {
|
||||
onreaderLoaded();
|
||||
}
|
||||
|
||||
function onreaderLoaded() {
|
||||
readerLoaded = true;
|
||||
Reader = fb.contacts;
|
||||
document.dispatchEvent(new CustomEvent(READER_LOADED_EV));
|
||||
}
|
||||
|
||||
function setIndex(index) {
|
||||
Reader.dsIndex = index;
|
||||
isIndexDirty = false;
|
||||
}
|
||||
|
||||
function datastore() {
|
||||
return Reader.datastore;
|
||||
}
|
||||
|
||||
function index() {
|
||||
return Reader.dsIndex;
|
||||
}
|
||||
|
||||
function defaultFunction(target) {
|
||||
var args = [];
|
||||
for (var j = 1; j < arguments.length; j++) {
|
||||
args.push(arguments[j]);
|
||||
}
|
||||
if (!readerLoaded) {
|
||||
document.addEventListener(READER_LOADED_EV, function rd_loaded() {
|
||||
document.removeEventListener(READER_LOADED_EV, rd_loaded);
|
||||
Reader[target].apply(this, args);
|
||||
});
|
||||
}
|
||||
else {
|
||||
// As the reader load will overwrite those functions probably this
|
||||
// will never be called
|
||||
/*jshint validthis:true */
|
||||
Reader[target].apply(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
function initError(outRequest, error) {
|
||||
outRequest.failed(error);
|
||||
}
|
||||
|
||||
// Ensures a proper error object is returned
|
||||
function safeError(err) {
|
||||
if (err && err.name) {
|
||||
return err;
|
||||
}
|
||||
return {
|
||||
name: 'UnknownError'
|
||||
};
|
||||
}
|
||||
|
||||
// Creates a default handler for errors
|
||||
function defaultError(request) {
|
||||
return defaultErrorCb.bind(null, request);
|
||||
}
|
||||
|
||||
// Creates a default handler for success
|
||||
function defaultSuccess(request) {
|
||||
return defaultSuccessCb.bind(null, request);
|
||||
}
|
||||
|
||||
function defaultErrorCb(request, error) {
|
||||
request.failed(safeError(error));
|
||||
}
|
||||
|
||||
function defaultSuccessCb(request, result) {
|
||||
request.done(result);
|
||||
}
|
||||
|
||||
function doSave(obj, outRequest) {
|
||||
LazyLoader.load([TEL_INDEXER_JS, PHONE_MATCHER_JS,
|
||||
BINARY_SEARCH_JS] , function() {
|
||||
var uid = obj.uid;
|
||||
datastore().add(obj, uid).then(function success() {
|
||||
indexByPhone(obj, uid);
|
||||
isIndexDirty = true;
|
||||
outRequest.done();
|
||||
}, function error(err) {
|
||||
if (err.name === 'ConstraintError') {
|
||||
err = {
|
||||
name: contacts.ALREADY_EXISTS
|
||||
};
|
||||
}
|
||||
outRequest.failed(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function indexByPhone(obj, newId) {
|
||||
// Update index by tel
|
||||
// As this is populated by FB importer we don't need to have
|
||||
// extra checks
|
||||
if (Array.isArray(obj.tel)) {
|
||||
obj.tel.forEach(function(aTel) {
|
||||
var variants = SimplePhoneMatcher.generateVariants(aTel.value);
|
||||
|
||||
variants.forEach(function(aVariant) {
|
||||
index().byTel[aVariant] = newId;
|
||||
});
|
||||
// To avoid the '+' char
|
||||
TelIndexer.index(index().treeTel, aTel.value.substring(1), newId);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function reIndexByPhone(oldObj, newObj, dsId) {
|
||||
removePhoneIndex(oldObj);
|
||||
indexByPhone(newObj, dsId);
|
||||
TelIndexer.orderTree(index().treeTel);
|
||||
}
|
||||
|
||||
function removePhoneIndex(deletedFriend) {
|
||||
// Need to update the tel indexes
|
||||
if (Array.isArray(deletedFriend.tel)) {
|
||||
deletedFriend.tel.forEach(function(aTel) {
|
||||
TelIndexer.remove(index().treeTel, aTel.value.substring(1));
|
||||
|
||||
var variants = SimplePhoneMatcher.generateVariants(aTel.value);
|
||||
variants.forEach(function(aVariant) {
|
||||
delete index().byTel[aVariant];
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to save FB Friend Information
|
||||
*
|
||||
*/
|
||||
contacts.save = function(obj) {
|
||||
var retRequest = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function save() {
|
||||
contacts.init(function() {
|
||||
doSave(obj, retRequest);
|
||||
},
|
||||
function(err) {
|
||||
initError(retRequest, err);
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return retRequest;
|
||||
};
|
||||
|
||||
/**
|
||||
* Allows to update FB Friend Information
|
||||
*
|
||||
*
|
||||
*/
|
||||
contacts.update = function(obj) {
|
||||
var retRequest = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function save() {
|
||||
contacts.init(function() {
|
||||
doUpdate(obj, retRequest);
|
||||
},
|
||||
function(err) {
|
||||
initError(retRequest, err);
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return retRequest;
|
||||
};
|
||||
|
||||
function doUpdate(obj, outRequest) {
|
||||
LazyLoader.load([TEL_INDEXER_JS, PHONE_MATCHER_JS,
|
||||
BINARY_SEARCH_JS], function() {
|
||||
var uid = obj.uid;
|
||||
|
||||
var successCb = successUpdate.bind(null, outRequest);
|
||||
var errorCb = errorUpdate.bind(null, outRequest, uid);
|
||||
|
||||
// It is necessary to get the old object and delete old indexes
|
||||
datastore().get(uid).then(function success(oldObj) {
|
||||
if (!oldObj) {
|
||||
errorCb({
|
||||
name: contacts.UID_NOT_FOUND
|
||||
});
|
||||
return;
|
||||
}
|
||||
reIndexByPhone(oldObj, obj, uid);
|
||||
datastore().put(obj, uid).then(function success() {
|
||||
return datastore().put(index(), INDEX_ID);
|
||||
}, errorCb).then(successCb, errorCb);
|
||||
}, errorCb); // datastore.get
|
||||
});
|
||||
}
|
||||
|
||||
function successUpdate(outRequest) {
|
||||
outRequest.done();
|
||||
}
|
||||
|
||||
function errorUpdate(outRequest, uid, error) {
|
||||
window.console.error('Error while updating datastore for: ', uid);
|
||||
outRequest.failed(error);
|
||||
}
|
||||
|
||||
function doRemove(uid, outRequest, forceFlush) {
|
||||
LazyLoader.load([TEL_INDEXER_JS, PHONE_MATCHER_JS,
|
||||
BINARY_SEARCH_JS], function() {
|
||||
var errorCb = errorRemove.bind(null, outRequest, uid);
|
||||
var objToDelete;
|
||||
|
||||
datastore().get(uid).then(function success_get_remove(object) {
|
||||
objToDelete = object;
|
||||
if (!objToDelete) {
|
||||
errorRemove(outRequest, uid, {
|
||||
name: contacts.UID_NOT_FOUND
|
||||
});
|
||||
return;
|
||||
}
|
||||
datastore().remove(uid).then(function success_rm(removed) {
|
||||
successRemove(outRequest, objToDelete, forceFlush, removed);
|
||||
}, errorCb);
|
||||
}, errorCb);
|
||||
}); // LazyLoader.load
|
||||
}
|
||||
|
||||
// Needs to update the index data conveniently
|
||||
function successRemove(outRequest, deletedFriend, forceFlush, removed) {
|
||||
if (removed === true) {
|
||||
isIndexDirty = true;
|
||||
|
||||
removePhoneIndex(deletedFriend);
|
||||
if (forceFlush) {
|
||||
var flushReq = fb.contacts.flush();
|
||||
|
||||
flushReq.onsuccess = function() {
|
||||
isIndexDirty = false;
|
||||
outRequest.done(true);
|
||||
};
|
||||
flushReq.onerror = function() {
|
||||
outRequest.failed(flushReq.error);
|
||||
};
|
||||
}
|
||||
else {
|
||||
outRequest.done(true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
outRequest.done(false);
|
||||
}
|
||||
}
|
||||
|
||||
function errorRemove(outRequest, uid, error) {
|
||||
error = safeError(error);
|
||||
window.console.error('FB Data: Error while removing ', uid, ': ',
|
||||
error.name);
|
||||
outRequest.failed(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to remove FB contact from the DB
|
||||
*
|
||||
*/
|
||||
contacts.remove = function(uid, flush) {
|
||||
var hasToFlush = (flush === true ? flush : false);
|
||||
var retRequest = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function remove() {
|
||||
contacts.init(function() {
|
||||
doRemove(uid, retRequest, hasToFlush);
|
||||
},
|
||||
function(err) {
|
||||
initError(retRequest, err);
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return retRequest;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes all the FB Friends and the index
|
||||
*
|
||||
* The index is restored as empty
|
||||
*
|
||||
*/
|
||||
contacts.clear = function() {
|
||||
var outRequest = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function clear() {
|
||||
contacts.init(function() {
|
||||
doClear(outRequest);
|
||||
},
|
||||
function(err) {
|
||||
initError(outRequest, err);
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return outRequest;
|
||||
};
|
||||
|
||||
function doClear(outRequest) {
|
||||
datastore().clear().then(function success() {
|
||||
setIndex(null);
|
||||
// TODO:
|
||||
// This is working but there are open questions on the mailing list
|
||||
datastore().put(index(), INDEX_ID).then(defaultSuccess(outRequest),
|
||||
function error(err) {
|
||||
err = safeError(err);
|
||||
window.console.error('Error while re-creating the index: ',
|
||||
err.name);
|
||||
outRequest.failed(err);
|
||||
}
|
||||
);
|
||||
}, defaultError(outRequest));
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists the index on the datastore
|
||||
*
|
||||
*/
|
||||
contacts.flush = function() {
|
||||
var outRequest = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function do_Flush() {
|
||||
if (!(datastore()) || !isIndexDirty) {
|
||||
window.console.warn(
|
||||
'The datastore has not been initialized or is not dirty');
|
||||
outRequest.done();
|
||||
return;
|
||||
}
|
||||
|
||||
TelIndexer.orderTree(index().treeTel);
|
||||
datastore().put(index(), INDEX_ID).then(
|
||||
defaultSuccess(outRequest),
|
||||
defaultError(outRequest));
|
||||
}, 0);
|
||||
|
||||
return outRequest;
|
||||
};
|
||||
|
||||
})();
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
'use strict';
|
||||
|
||||
/* global fb */
|
||||
|
||||
/* jshint ignore:start */
|
||||
var fb = this.fb || {};
|
||||
|
||||
// In an only worker execution context this would not be necessary
|
||||
var self = this;
|
||||
fb.utils = this.fb.utils || {};
|
||||
/* jshint ignore:end */
|
||||
|
||||
fb.DEFAULT_TIMEOUT = 30000;
|
||||
|
||||
// Runs a query against Facebook FQL. Callback is a string!!
|
||||
fb.utils.runQuery = function(query, callback, access_token) {
|
||||
// Auxiliary function for canceling a request
|
||||
function QueryRequest() {
|
||||
this.cancel = function() {
|
||||
if (typeof this.oncancel === 'function') {
|
||||
window.setTimeout(function() {
|
||||
this.oncancel();
|
||||
}.bind(this), 0);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var outReq = new QueryRequest();
|
||||
|
||||
var queryService = 'https://graph.facebook.com/fql?q=';
|
||||
queryService += encodeURIComponent(query);
|
||||
|
||||
var params = ['access_token' + '=' + access_token,
|
||||
'format=json'];
|
||||
|
||||
var queryParams = params.join('&');
|
||||
|
||||
var remote = queryService + '&' + queryParams;
|
||||
|
||||
var xhr = new XMLHttpRequest({
|
||||
mozSystem: true
|
||||
});
|
||||
|
||||
// To enable xhr.abort if user cancels
|
||||
outReq.xhr = xhr;
|
||||
outReq.oncancel = function() {
|
||||
this.xhr.abort();
|
||||
};
|
||||
|
||||
xhr.open('GET', remote, true);
|
||||
xhr.responseType = 'json';
|
||||
|
||||
xhr.timeout = fb.operationsTimeout || fb.DEFAULT_TIMEOUT;
|
||||
|
||||
xhr.onload = function(e) {
|
||||
if (xhr.status === 200 || xhr.status === 400 || xhr.status === 0) {
|
||||
if (callback && typeof callback.success === 'function') {
|
||||
self.setTimeout(function() {
|
||||
callback.success(xhr.response);
|
||||
},0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
self.console.error('FB: HTTP error executing query. ',
|
||||
query, ' Status: ', xhr.status);
|
||||
if (callback && typeof callback.error === 'function') {
|
||||
self.setTimeout(callback.error, 0);
|
||||
}
|
||||
}
|
||||
}; // onload
|
||||
|
||||
xhr.ontimeout = function(e) {
|
||||
self.console.error('FB: Timeout!!! while executing query', query);
|
||||
if (callback && typeof callback.timeout === 'function') {
|
||||
self.setTimeout(callback.timeout, 0);
|
||||
}
|
||||
}; // ontimeout
|
||||
|
||||
xhr.onerror = function(e) {
|
||||
self.console.error('FB: Error while executing query: ', query,
|
||||
': ', e);
|
||||
if (callback && typeof callback.error === 'function') {
|
||||
self.setTimeout(function() {
|
||||
callback.error(e);
|
||||
},0);
|
||||
}
|
||||
}; // onerror
|
||||
|
||||
xhr.send();
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
/**
|
||||
* Obtains a img DOM Element with the Contact's img
|
||||
*
|
||||
*/
|
||||
fb.utils.getFriendPicture = function(uid, callback, access_token,
|
||||
targetPictureSize) {
|
||||
// Access token is necessary just in case the image is not public
|
||||
// When passing an access token to FB https must be used
|
||||
|
||||
var imgSrc = 'https://graph.facebook.com/' + uid + '/picture?';
|
||||
|
||||
var params = [
|
||||
'width=' + targetPictureSize,
|
||||
'access_token=' + access_token,
|
||||
't=' + Date.now()
|
||||
];
|
||||
|
||||
var imgService = imgSrc + params.join('&');
|
||||
|
||||
var xhr = new XMLHttpRequest({
|
||||
mozSystem: true
|
||||
});
|
||||
xhr.open('GET', imgService, true);
|
||||
xhr.responseType = 'blob';
|
||||
|
||||
xhr.timeout = fb.operationsTimeout || fb.DEFAULT_TIMEOUT;
|
||||
|
||||
xhr.onload = function(e) {
|
||||
if (xhr.status === 200 || xhr.status === 0) {
|
||||
var mblob = e.target.response;
|
||||
if (typeof callback === 'function') {
|
||||
self.setTimeout(function() {
|
||||
callback(mblob);
|
||||
},0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
self.console.error('FB: HTTP error retrieving img for uid: ',
|
||||
uid, ' Status: ', xhr.status);
|
||||
if (typeof callback === 'function') {
|
||||
self.setTimeout(function() {
|
||||
callback(null);
|
||||
},0);
|
||||
}
|
||||
}
|
||||
}; // onload
|
||||
|
||||
xhr.ontimeout = function(e) {
|
||||
self.console.error('FB: Timeout!!! while retrieving img for uid: ', uid);
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
self.setTimeout(function() {
|
||||
callback(null);
|
||||
},0);
|
||||
}
|
||||
}; // ontimeout
|
||||
|
||||
xhr.onerror = function(e) {
|
||||
self.console.error('FB: Error while retrieving img for uid: ', uid,
|
||||
'Error: ', e);
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
self.setTimeout(function() {
|
||||
callback(null);
|
||||
},0);
|
||||
}
|
||||
}; // onerror
|
||||
|
||||
xhr.send();
|
||||
};
|
||||
|
|
@ -0,0 +1,526 @@
|
|||
'use strict';
|
||||
|
||||
/* global ImportStatusData, oauthflow, utils */
|
||||
/* Ignoring function declaration in loops */
|
||||
/* jshint -W083 */
|
||||
|
||||
var fb = window.fb || {};
|
||||
window.fb = fb;
|
||||
|
||||
(function(document) {
|
||||
var Utils = fb.utils || {};
|
||||
fb.utils = Utils;
|
||||
|
||||
var FRIEND_COUNT_QUERY = 'select friend_count from user where uid=me()';
|
||||
|
||||
var CACHE_FRIENDS_KEY = Utils.CACHE_FRIENDS_KEY = 'numFacebookFriends';
|
||||
var LAST_UPDATED_KEY = Utils.LAST_UPDATED_KEY = 'lastUpdatedTime';
|
||||
Utils.ALARM_ID_KEY = 'nextAlarmId';
|
||||
Utils.SCHEDULE_SYNC_KEY = 'facebookShouldHaveScheduledAt';
|
||||
|
||||
var REDIRECT_LOGOUT_URI = window.oauthflow ?
|
||||
oauthflow.params.facebook.redirectLogout : '';
|
||||
var STORAGE_KEY = Utils.TOKEN_DATA_KEY = 'tokenData';
|
||||
|
||||
// For controlling data synchronization
|
||||
Utils.setLastUpdate = function(value, cb) {
|
||||
window.asyncStorage.setItem(LAST_UPDATED_KEY, {
|
||||
data: value
|
||||
}, cb);
|
||||
};
|
||||
|
||||
Utils.getLastUpdate = function(callback) {
|
||||
window.asyncStorage.getItem(LAST_UPDATED_KEY, function(obj) {
|
||||
var out = 0;
|
||||
if (obj) {
|
||||
out = obj.data || out;
|
||||
}
|
||||
if (typeof callback === 'function') {
|
||||
callback(out);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Utils.getNonCacheableUrl = function(url) {
|
||||
if (url.indexOf('?') === -1) {
|
||||
url += '?';
|
||||
}
|
||||
|
||||
return url + '&burst_cache=' + Date.now();
|
||||
};
|
||||
|
||||
|
||||
Utils.getContactData = function(cid) {
|
||||
var outReq = new Utils.Request();
|
||||
|
||||
var req = navigator.mozContacts.find({
|
||||
filterBy: ['id'],
|
||||
filterValue: cid,
|
||||
filterOp: 'equals'
|
||||
});
|
||||
|
||||
req.onsuccess = function(e) {
|
||||
if (e.target.result && e.target.result.length > 0) {
|
||||
outReq.done(e.target.result[0]);
|
||||
}
|
||||
else {
|
||||
outReq.done(null);
|
||||
}
|
||||
};
|
||||
|
||||
req.onerror = function(e) {
|
||||
outReq.failed(e.target.error);
|
||||
};
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
// Returns the mozContact associated to a UID in FB
|
||||
Utils.getMozContact = function(uid) {
|
||||
var outReq = new Utils.Request();
|
||||
|
||||
window.setTimeout(function get_mozContact_ByUid() {
|
||||
fb.getMozContactByUid(uid,
|
||||
function onsuccess(result) {
|
||||
if (Array.isArray(result) && result.length > 0) {
|
||||
outReq.done(result[0]);
|
||||
} else {
|
||||
outReq.done(null);
|
||||
}
|
||||
},
|
||||
function onerror(e) {
|
||||
outReq.failed(e.target.error);
|
||||
}
|
||||
);
|
||||
}, 0);
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
// Returns the number of mozContacts associated to a UID in FB
|
||||
Utils.getNumberMozContacts = function(uid) {
|
||||
var outReq = new Utils.Request();
|
||||
|
||||
window.setTimeout(function get_mozContact_ByUid() {
|
||||
fb.getMozContactByUid(uid,
|
||||
function onsuccess(result) {
|
||||
if (Array.isArray(result)) {
|
||||
outReq.done(result.length);
|
||||
} else {
|
||||
outReq.done(0);
|
||||
}
|
||||
},
|
||||
function onerror(error) {
|
||||
outReq.failed(error);
|
||||
}
|
||||
);
|
||||
},0);
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
Utils.getAllFbContacts = function() {
|
||||
var outReq = new Utils.Request();
|
||||
|
||||
window.setTimeout(function get_all_fb_contacts() {
|
||||
var filter = {
|
||||
filterValue: fb.CATEGORY,
|
||||
filterOp: 'contains',
|
||||
filterBy: ['category']
|
||||
};
|
||||
|
||||
var req = navigator.mozContacts.find(filter);
|
||||
|
||||
req.onsuccess = function(e) {
|
||||
outReq.done(e.target.result);
|
||||
};
|
||||
|
||||
req.onerror = function(e) {
|
||||
outReq.failed(e.target.error);
|
||||
};
|
||||
}, 0);
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
// On the device
|
||||
Utils.getNumFbContacts = function() {
|
||||
var outReq = new Utils.Request();
|
||||
|
||||
window.setTimeout(function get_num_fb_contacts() {
|
||||
var req = fb.contacts.getLength();
|
||||
|
||||
req.onsuccess = function() {
|
||||
outReq.done(req.result);
|
||||
};
|
||||
|
||||
req.onerror = function() {
|
||||
outReq.failed(req.error);
|
||||
};
|
||||
}, 0);
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
// Requests the number remotely
|
||||
Utils.getNumFbFriends = function(callback, access_token) {
|
||||
fb.utils.runQuery(FRIEND_COUNT_QUERY, callback, access_token);
|
||||
};
|
||||
|
||||
Utils.getCachedAccessToken = function(callback) {
|
||||
ImportStatusData.get(STORAGE_KEY).then(function(data) {
|
||||
var out = null;
|
||||
|
||||
if (data) {
|
||||
out = data.access_token || null;
|
||||
}
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback(out);
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
Utils.setCachedAccessToken = function(data, callback) {
|
||||
ImportStatusData.put(STORAGE_KEY, data).then(function done() {
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
}, function error() {
|
||||
console.error('Error while saving access token');
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Obtains the number locally
|
||||
Utils.getCachedNumFbFriends = function(callback) {
|
||||
ImportStatusData.get(CACHE_FRIENDS_KEY).then(function(data) {
|
||||
if (typeof callback === 'function' && typeof data === 'number') {
|
||||
callback(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Utils.setCachedNumFriends = function(value, cb) {
|
||||
ImportStatusData.put(CACHE_FRIENDS_KEY, value).then(cb);
|
||||
};
|
||||
|
||||
// Removes cached number of friends from datastore to keep state of sync.
|
||||
Utils.removeCachedNumFriends = function(callback) {
|
||||
ImportStatusData.remove(CACHE_FRIENDS_KEY).then(function() {
|
||||
typeof callback === 'function' && callback();
|
||||
});
|
||||
};
|
||||
|
||||
Utils.getImportChecked = function(callback) {
|
||||
// If we have an access token Import should be checked
|
||||
Utils.getCachedAccessToken(function(access_token) {
|
||||
var out = 'logged-out';
|
||||
|
||||
if (access_token) {
|
||||
out = 'logged-in';
|
||||
}
|
||||
else {
|
||||
// In this case is needed to know whether the access_token has
|
||||
// been invalidated
|
||||
Utils.getCachedNumFbFriends(function(value) {
|
||||
if (value) {
|
||||
out = 'renew-pwd';
|
||||
if (typeof callback === 'function') {
|
||||
callback(out);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback(out);
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Obtains the number locally (cached) and tries to get them remotely
|
||||
Utils.numFbFriendsData = function(callback) {
|
||||
var localCb = callback.local;
|
||||
var remoteCb = callback.remote;
|
||||
|
||||
Utils.getCachedNumFbFriends(localCb);
|
||||
|
||||
function auxCallback(response) {
|
||||
if (response.data && response.data[0] &&
|
||||
response.data[0].friend_count) {
|
||||
remoteCb(response.data[0].friend_count);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof remoteCb === 'function' && navigator.onLine === true) {
|
||||
var remoteCallbacks = {
|
||||
success: auxCallback,
|
||||
error: null,
|
||||
timeout: null
|
||||
};
|
||||
Utils.getCachedAccessToken(function(access_token) {
|
||||
if (access_token) {
|
||||
Utils.getNumFbFriends(remoteCallbacks, access_token);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Clears all Fb data (use with caution!!)
|
||||
Utils.clearFbData = function() {
|
||||
// First step all Contacts which are FB Friends are obtained
|
||||
// then those not linked are directly removed
|
||||
// Those linked are unlinked
|
||||
|
||||
var outReq = new Utils.Request();
|
||||
|
||||
window.setTimeout(function do_clearFbData() {
|
||||
// First a clear request is issued
|
||||
var ireq = fb.contacts.clear();
|
||||
|
||||
ireq.onsuccess = function() {
|
||||
var req = Utils.getAllFbContacts();
|
||||
|
||||
req.onsuccess = function() {
|
||||
var cleaner = new Utils.FbContactsCleaner(req.result, 'clear');
|
||||
// And now success notification is sent
|
||||
outReq.done(cleaner);
|
||||
// The cleaning activity should be starting immediately
|
||||
window.setTimeout(cleaner.start, 0);
|
||||
};
|
||||
|
||||
req.onerror = function() {
|
||||
window.console.error('FB Clean. Error retrieving FB Contacts');
|
||||
outReq.failed(req.error);
|
||||
};
|
||||
};
|
||||
|
||||
ireq.onerror = function(e) {
|
||||
window.console.error('Error while clearing the FB Cache');
|
||||
outReq.failed(ireq.error);
|
||||
};
|
||||
|
||||
},0);
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
Utils.logout = function() {
|
||||
var outReq = new Utils.Request();
|
||||
|
||||
// Duration of the timer that checks window close by the user (1/2 sec)
|
||||
var WINDOW_TIMER_INTERVAL = 500;
|
||||
// Maximum time we are going to wait for a logout operation (5 secs)
|
||||
var LOGOUT_TIMEOUT = 5000;
|
||||
var MAX_TIMER_TICKS = LOGOUT_TIMEOUT / WINDOW_TIMER_INTERVAL;
|
||||
|
||||
window.setTimeout(function do_logout() {
|
||||
Utils.getCachedAccessToken(function getAccessToken(access_token) {
|
||||
if (access_token) {
|
||||
var logoutService = 'https://www.facebook.com/logout.php?';
|
||||
var params = [
|
||||
'next' + '=' + encodeURIComponent(REDIRECT_LOGOUT_URI),
|
||||
'access_token' + '=' + access_token
|
||||
];
|
||||
|
||||
var logoutParams = params.join('&');
|
||||
var logoutUrl = logoutService + logoutParams;
|
||||
var loggedOut = false;
|
||||
|
||||
var m_listen = function(e) {
|
||||
if (e.origin !== fb.CONTACTS_APP_ORIGIN) {
|
||||
return;
|
||||
}
|
||||
if (e.data === 'closed') {
|
||||
loggedOut = true;
|
||||
ImportStatusData.remove(STORAGE_KEY);
|
||||
outReq.done();
|
||||
}
|
||||
e.stopImmediatePropagation();
|
||||
window.removeEventListener('message', m_listen);
|
||||
};
|
||||
|
||||
window.addEventListener('message', m_listen);
|
||||
|
||||
var logoutWindow = window.open(logoutUrl, '', 'dialog');
|
||||
// This timer is run in order to monitor whether the window is
|
||||
// closed by the user during the logout process (bug 1084224).
|
||||
// This will have to be removed once bug 966216 is fixed
|
||||
var timerTicks = 0;
|
||||
var timerId = window.setInterval(function() {
|
||||
timerTicks++;
|
||||
var closed = (logoutWindow.closed === true);
|
||||
var timedOut = (timerTicks >= MAX_TIMER_TICKS);
|
||||
|
||||
if (loggedOut || closed || timedOut) {
|
||||
window.clearInterval(timerId);
|
||||
window.removeEventListener('message', m_listen);
|
||||
}
|
||||
|
||||
if (closed && !loggedOut) {
|
||||
outReq.failed('UserCancelled');
|
||||
}
|
||||
else if (timedOut && !loggedOut) {
|
||||
outReq.failed('Timeout');
|
||||
}
|
||||
}, WINDOW_TIMER_INTERVAL);
|
||||
} // if
|
||||
else {
|
||||
outReq.done();
|
||||
}
|
||||
}); // cachedToken
|
||||
}, 0); // setTimeout
|
||||
|
||||
return outReq;
|
||||
|
||||
}; // logout
|
||||
|
||||
|
||||
// FbContactsCleaner Object
|
||||
// Mode can be 'update' or 'cleanAll'
|
||||
Utils.FbContactsCleaner = function(contacts, pmode) {
|
||||
this.lcontacts = contacts;
|
||||
var total = contacts.length;
|
||||
var next = 0;
|
||||
var self = this;
|
||||
var CHUNK_SIZE = 5;
|
||||
var numResponses = 0;
|
||||
var mustUpdate = (pmode === 'update');
|
||||
var notifyClean = false;
|
||||
|
||||
var mustHold = false;
|
||||
var holded = false;
|
||||
var mustFinish = false;
|
||||
|
||||
this.start = function() {
|
||||
mustHold = holded = mustFinish = false;
|
||||
|
||||
if (total > 0) {
|
||||
cleanContacts(0);
|
||||
}
|
||||
else if (typeof self.onsuccess === 'function') {
|
||||
window.setTimeout(self.onsuccess);
|
||||
}
|
||||
};
|
||||
|
||||
this.hold = function() {
|
||||
mustHold = true;
|
||||
};
|
||||
|
||||
this.finish = function() {
|
||||
mustFinish = true;
|
||||
|
||||
if (holded) {
|
||||
notifySuccess();
|
||||
}
|
||||
};
|
||||
|
||||
this.resume = function() {
|
||||
mustHold = holded = mustFinish = false;
|
||||
|
||||
window.setTimeout(function resume_clean() {
|
||||
cleanContacts(next);
|
||||
});
|
||||
};
|
||||
|
||||
function successHandler(e) {
|
||||
if (notifyClean || typeof self.oncleaned === 'function') {
|
||||
notifyClean = true;
|
||||
// Avoiding race condition so the cleaned element is cached
|
||||
var cleaned = e.target.number;
|
||||
window.setTimeout(function() {
|
||||
self.oncleaned(cleaned);
|
||||
},0);
|
||||
}
|
||||
continueCb();
|
||||
}
|
||||
|
||||
function errorHandler(contactid, error) {
|
||||
if (typeof self.onerror === 'function') {
|
||||
self.onerror(contactid, error);
|
||||
}
|
||||
|
||||
continueCb();
|
||||
}
|
||||
|
||||
function cleanContacts(from) {
|
||||
for (var idx = from; idx < (from + CHUNK_SIZE) && idx < total; idx++) {
|
||||
var contact = contacts[idx];
|
||||
var number = idx;
|
||||
var req, fbContact;
|
||||
if (fb.isFbLinked(contact)) {
|
||||
if (mustUpdate) {
|
||||
fbContact = new fb.Contact(contact);
|
||||
req = fbContact.unlink('hard');
|
||||
}
|
||||
else {
|
||||
// Here we only need to mark this contact as unlinked
|
||||
// as the FB Data would have been already removed
|
||||
fb.unlinkClearAll(contact);
|
||||
req = navigator.mozContacts.save(
|
||||
utils.misc.toMozContact(contact));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (mustUpdate) {
|
||||
fbContact = new fb.Contact(contact);
|
||||
req = fbContact.remove();
|
||||
}
|
||||
else {
|
||||
req = navigator.mozContacts.remove(
|
||||
utils.misc.toMozContact(contact));
|
||||
}
|
||||
}
|
||||
req.number = number;
|
||||
req.onsuccess = successHandler;
|
||||
req.onerror = function(e) {
|
||||
errorHandler(contact.id, e.target.error);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function notifySuccess() {
|
||||
if (typeof self.onsuccess === 'function') {
|
||||
window.setTimeout(self.onsuccess);
|
||||
}
|
||||
}
|
||||
|
||||
function finishHandler() {
|
||||
// It is needed to flush in order to properly update the index
|
||||
var req = fb.contacts.flush();
|
||||
req.onsuccess = notifySuccess;
|
||||
req.onerror = function cleaner_flushError() {
|
||||
errorHandler(null, req.error);
|
||||
};
|
||||
}
|
||||
|
||||
function continueCb() {
|
||||
next++;
|
||||
numResponses++;
|
||||
if (next < total && numResponses === CHUNK_SIZE) {
|
||||
numResponses = 0;
|
||||
if (!mustHold && !mustFinish) {
|
||||
cleanContacts(next);
|
||||
}
|
||||
else if (mustFinish && !holded) {
|
||||
finishHandler();
|
||||
}
|
||||
|
||||
if (mustHold) {
|
||||
holded = true;
|
||||
}
|
||||
}
|
||||
else if (next >= total) {
|
||||
// End has been reached
|
||||
finishHandler();
|
||||
}
|
||||
} // function
|
||||
}; // FbContactsCleaner
|
||||
|
||||
})(document);
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
;'use strict';var FriendListRenderer=(function(){var orderCriteria={firstName:['givenName','familyName','email1'],lastName:['familyName','givenName','email1']};var HEADER_LETTERS=['ABCDEFGHIJKLMNOPQRSTUVWXYZ','ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ','АБВГДЂЕЁЖЗИЙЈКЛЉМНЊОПРСТЋУФХЦЧЏШЩЭЮЯ'].join('');var defaults={container:'#groups-list',orderBy:'lastName'};var finishCb;var totalContacts;var lock;var CHUNK_SIZE=10;var render=function render(contacts,cb,options){for(var key in defaults){if(typeof options[key]==='undefined'){options[key]=defaults[key];}}
|
||||
finishCb=cb;totalContacts=contacts.length;lock=navigator.requestWakeLock('cpu');doRender(contacts,cb,options);};var doRender=function doRender(contacts,cb,options){var groupsList=document.querySelector(options.container);var orderBy=options.orderBy;var order=orderCriteria[orderBy];contacts.sort(function(a,b){return getStringToBeOrdered(a,order).localeCompare(getStringToBeOrdered(b,order));});var groups={};contacts.forEach(function(contact){var groupName=getGroupName(contact,order);if(!groups[groupName]){groups[groupName]=[];}
|
||||
groups[groupName].push(contact);});var notRenderedParagraph=groupsList.querySelector('[data-order-by="'+
|
||||
(orderBy==='firstName'?'lastName':'firstName')+'"]');if(notRenderedParagraph){notRenderedParagraph.parentNode.removeChild(notRenderedParagraph);}
|
||||
var letterStart=0;doRenderGroupChunk(letterStart,HEADER_LETTERS[letterStart],groupsList,groups);};function doRenderGroupChunk(index,group,groupsList,groups){renderGroup(groupsList,group,groups[group],function(fragment){if(fragment){groupsList.appendChild(fragment);}
|
||||
fragment=null;var headerLettersLength=HEADER_LETTERS.length;if(index+1<=headerLettersLength){window.setTimeout(function renderNextGroup(){doRenderGroupChunk(index+1,HEADER_LETTERS[index+1],groupsList,groups);});}
|
||||
else if(group!='#'){window.setTimeout(function renderNextGroup(){doRenderGroupChunk(index+1,'#',groupsList,groups);});}
|
||||
else{groupsList.removeChild(groupsList.firstElementChild);if(typeof finishCb==='function'){window.setTimeout(finishCb,totalContacts*2);}
|
||||
if(lock){lock.unlock();}}});}
|
||||
function doRenderGroupItems(from,groupsList,group,friends,element,cb){var end=from+CHUNK_SIZE;var list=element.children[1];for(var i=from;i<end&&i<friends.length;i++){var friend=friends[i];if(friend.search&&friend.search.length>0){var box=utils.misc.getPreferredPictureBox();friend.picwidth=box.width;friend.picheight=box.height;friend.search=Normalizer.toAscii(friend.search);utils.templates.append(list,friend);}}
|
||||
if(i<friends.length){window.setTimeout(function renderNextChunk(){doRenderGroupItems(end,groupsList,group,friends,element,cb);});}
|
||||
else{list.removeChild(list.firstElementChild);cb();}}
|
||||
function renderGroup(groupsList,group,friends,cb){if(!friends||friends.length===0){window.setTimeout(cb);return;}
|
||||
var fragment=document.createDocumentFragment();var element=utils.templates.append(groupsList,{group:group},fragment);doRenderGroupItems(0,groupsList,group,friends,element,function(){cb(fragment);});}
|
||||
function getStringToBeOrdered(contact,order){var ret=contact.search;if(!ret){ret=[];order.forEach(function(field){ret.push(getValue(contact,field));});ret=contact.search=ret.join(' ');}
|
||||
return ret;}
|
||||
function getValue(contact,field){var out=contact[field];if(out){out=Array.isArray(out)?out[0]:out;}else{out='';}
|
||||
return out;}
|
||||
function getGroupName(contact,order){var ret=getStringToBeOrdered(contact,order);ret=ret.charAt(0).toUpperCase();ret=ret.replace(/[ÁÀ]/ig,'A');ret=ret.replace(/[ÉÈ]/ig,'E');ret=ret.replace(/[ÍÌ]/ig,'I');ret=ret.replace(/[ÓÒ]/ig,'O');ret=ret.replace(/[ÚÙ]/ig,'U');if(HEADER_LETTERS.indexOf(ret)<0){ret='#';}
|
||||
return ret;}
|
||||
return{'render':render};})();
|
||||
|
|
@ -0,0 +1,475 @@
|
|||
/* globals Rest */
|
||||
|
||||
/* exported GmailConnector */
|
||||
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
Gmail Contacts connector
|
||||
|
||||
Provides the capabilities to connect to the Gmail Contacts service
|
||||
and return the contacts on a readable and importable format.
|
||||
*/
|
||||
var GmailConnector = (function GmailConnector() {
|
||||
|
||||
// Google contacts service end point,
|
||||
// force a huge number of contacts to not paginate :S
|
||||
var END_POINT =
|
||||
'https://www.google.com/m8/feeds/contacts/default/full/?max-results=10000';
|
||||
var GROUPS_END_POINT =
|
||||
'https://www.google.com/m8/feeds/groups/default/full/';
|
||||
var EXTRA_HEADERS = {
|
||||
'GData-Version': '3.0'
|
||||
};
|
||||
var GD_NAMESPACE = 'http://schemas.google.com/g/2005';
|
||||
|
||||
var CATEGORY = 'gmail';
|
||||
var URN_IDENTIFIER = 'urn:service:gmail:uid:';
|
||||
|
||||
// Will be used as a cache for the thumbnail url for each contact
|
||||
var photoUrls = {};
|
||||
|
||||
// In some cases we will need the access token, cache a copy
|
||||
var accessToken = null;
|
||||
|
||||
// We have a xml response from Google, all the entries in an array,
|
||||
// no matter if they are xml entries.
|
||||
// Despite of being a node, inject a 'uid' as this will be necessary
|
||||
// for the selection
|
||||
var nodeListToArray = function nodeListToArray(response) {
|
||||
var entries = response.getElementsByTagName('entry');
|
||||
var contacts = [];
|
||||
var numContacts = entries.length;
|
||||
for (var i = 0; i < numContacts; i++) {
|
||||
contacts.push(gContactToJson(entries[i]));
|
||||
}
|
||||
|
||||
return contacts;
|
||||
};
|
||||
|
||||
// Returns the object used to build the headers necesary by the service
|
||||
var buildRequestHeaders = function buildRequestHeaders(access_token) {
|
||||
var requestHeaders = EXTRA_HEADERS;
|
||||
requestHeaders.Authorization = 'OAuth ' + access_token;
|
||||
|
||||
return requestHeaders;
|
||||
};
|
||||
|
||||
// Gets a list of all contacts giving a valid access token
|
||||
var listAllContacts = function listAllContacts(access_token,
|
||||
callbacks) {
|
||||
// Copy the access_token
|
||||
accessToken = access_token;
|
||||
photoUrls = {};
|
||||
getContactsGroup(access_token, callbacks);
|
||||
};
|
||||
|
||||
var getContactsGroup = function getContactsGroup(access_token,
|
||||
callbacks) {
|
||||
var groupCallbacks = {
|
||||
success: function onSuccess(response) {
|
||||
// Locate the entry witch systemGroup id is 'Contacts'
|
||||
var feed = response.querySelector('feed');
|
||||
if (feed === null) {
|
||||
callbacks.error();
|
||||
return;
|
||||
}
|
||||
|
||||
var sgc = feed.querySelector('systemGroup[id="Contacts"]');
|
||||
if (sgc !== null) {
|
||||
var id = sgc.parentNode.querySelector('id').textContent;
|
||||
getContactsByGroup(id, access_token, callbacks);
|
||||
} else {
|
||||
callbacks.error();
|
||||
}
|
||||
},
|
||||
error: function(e) {
|
||||
if (e && e.status === 401) {
|
||||
// This is a token expired / invalid token problem
|
||||
window.console.warn('GMail Access token expired or invalid. ',
|
||||
'restarting flow!');
|
||||
callbacks.success({
|
||||
error: {
|
||||
code: 190
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
callbacks.error();
|
||||
}
|
||||
},
|
||||
timeout: callbacks.timeout
|
||||
};
|
||||
|
||||
return performAPIRequest(GROUPS_END_POINT, groupCallbacks, access_token);
|
||||
};
|
||||
|
||||
// Retrieve all the contacts for the specific groupId
|
||||
var getContactsByGroup = function getContactsByGroup(groupId, access_token,
|
||||
callbacks) {
|
||||
var listingCallbacks = {
|
||||
success: function onSuccess(response) {
|
||||
callbacks.success({
|
||||
'data': nodeListToArray(response)
|
||||
});
|
||||
},
|
||||
error: callbacks.error,
|
||||
timeout: callbacks.timeout
|
||||
};
|
||||
|
||||
var groupUrl = END_POINT + '&group=' + groupId;
|
||||
return performAPIRequest(groupUrl, listingCallbacks, access_token);
|
||||
};
|
||||
|
||||
// Given a Google contacts api url add the authentication and
|
||||
// extra headers to perform the correct request
|
||||
var performAPIRequest = function performAPIRequest(url, callbacks,
|
||||
access_token) {
|
||||
return Rest.get(url, callbacks, {
|
||||
'requestHeaders': buildRequestHeaders(access_token),
|
||||
'responseType': 'xml'
|
||||
});
|
||||
};
|
||||
|
||||
// Return the list of contacts on the device imported using this connector
|
||||
var listDeviceContacts = function listDeviceContacts(callbacks) {
|
||||
var filterOptions = {
|
||||
filterValue: CATEGORY,
|
||||
filterOp: 'contains',
|
||||
filterBy: ['category']
|
||||
};
|
||||
|
||||
var req = navigator.mozContacts.find(filterOptions);
|
||||
req.onsuccess = function() {
|
||||
callbacks.success(req.result);
|
||||
};
|
||||
req.onerror = function onError() {
|
||||
callbacks.success([]);
|
||||
};
|
||||
};
|
||||
|
||||
var getImporter = function getImporter(contactsList, access_token) {
|
||||
return new window.ContactsImporter(contactsList, access_token, this);
|
||||
};
|
||||
|
||||
var cleanContacts = function cleanContacts(contactsList, mode, cb) {
|
||||
var cleaner = new window.ContactsCleaner(contactsList);
|
||||
window.setTimeout(cleaner.start, 0);
|
||||
if (cb) {
|
||||
cb(cleaner);
|
||||
}
|
||||
};
|
||||
|
||||
var getValueForNode = function getValueForNode(doc, name, def) {
|
||||
var defaultValue = def || '';
|
||||
|
||||
if (doc == null || name == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
var node = doc.querySelector(name);
|
||||
|
||||
if (node && node.textContent) {
|
||||
return node.textContent;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
};
|
||||
|
||||
/*
|
||||
Given an xml contact entry from gmail contacts we will need to output
|
||||
a json object that contains the minimun viable information to display
|
||||
that contact into a list.
|
||||
|
||||
The fields for that minimun visualization object are:
|
||||
uid
|
||||
givenName
|
||||
familyName
|
||||
email1
|
||||
*/
|
||||
var adaptDataForShowing = function adaptDataForShowing(contact) {
|
||||
var output = {
|
||||
'uid': '-1',
|
||||
'givenName': '',
|
||||
'familyName': '',
|
||||
'email1': '',
|
||||
'contactPictureUri': ''
|
||||
};
|
||||
|
||||
output.uid = contact.uid;
|
||||
|
||||
if (contact.familyName) {
|
||||
output.familyName = contact.familyName;
|
||||
}
|
||||
|
||||
if (contact.email && contact.email.length > 0) {
|
||||
output.email1 = contact.email[0].value;
|
||||
}
|
||||
var tel;
|
||||
if (contact.tel && contact.tel.length > 0) {
|
||||
tel = contact.tel[0].value;
|
||||
}
|
||||
if(contact.givenName) {
|
||||
output.givenName = contact.givenName;
|
||||
} else if(!contact.familyName) {
|
||||
output.givenName = output.email1 || contact.org || tel;
|
||||
} else if(contact.familyName) {
|
||||
output.givenName = '' ;
|
||||
}
|
||||
|
||||
var photoUrl = buildContactPhotoURL(contact, accessToken);
|
||||
if (photoUrl) {
|
||||
output.contactPictureUri = photoUrl;
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
||||
|
||||
var adaptDataForSaving = function adaptDataForSaving(contact) {
|
||||
return contact;
|
||||
};
|
||||
|
||||
// Transform a Google contact entry into json format.
|
||||
// The json format is the same used in Contacts api ;P
|
||||
var gContactToJson = function gContactToJson(googleContact) {
|
||||
var output = {};
|
||||
|
||||
// This field will be needed for indexing within the
|
||||
// import process, not for the api
|
||||
output.uid = getUid(googleContact);
|
||||
|
||||
output.name = [getValueForNode(googleContact, 'title')];
|
||||
|
||||
// Store the photo url, not in the contact itself
|
||||
var photoUrl = googleContact.querySelector('link[type="image/*"]');
|
||||
if (photoUrl) {
|
||||
photoUrl = photoUrl.getAttribute('href');
|
||||
} else {
|
||||
// No image link
|
||||
photoUrl = '';
|
||||
}
|
||||
photoUrls[output.uid] = photoUrl;
|
||||
|
||||
var name = googleContact.querySelector('name');
|
||||
if (name) {
|
||||
var contactName = getValueForNode(name, 'givenName');
|
||||
if (contactName) {
|
||||
output.givenName = [contactName];
|
||||
}
|
||||
var contactFamilyName = getValueForNode(name, 'familyName');
|
||||
if (contactFamilyName) {
|
||||
output.familyName = [contactFamilyName];
|
||||
}
|
||||
var contactSuffix = getValueForNode(name, 'additionalName');
|
||||
if (contactSuffix) {
|
||||
output.additionalName = [contactSuffix];
|
||||
}
|
||||
}
|
||||
|
||||
output.email = parseEmails(googleContact);
|
||||
|
||||
output.adr = parseAddress(googleContact);
|
||||
|
||||
output.tel = parsePhones(googleContact);
|
||||
|
||||
var org = googleContact.querySelector('organization');
|
||||
if (org) {
|
||||
output.org = [getValueForNode(org, 'orgName')];
|
||||
output.jobTitle = [getValueForNode(org, 'orgTitle')];
|
||||
}
|
||||
|
||||
var bday = googleContact.querySelector('birthday');
|
||||
if (bday) {
|
||||
var bdayMS = Date.parse(bday.getAttribute('when'));
|
||||
if (!isNaN(bdayMS)) {
|
||||
output.bday = new Date(bdayMS);
|
||||
}
|
||||
}
|
||||
|
||||
var content = googleContact.querySelector('content');
|
||||
if (content) {
|
||||
output.note = [content.textContent];
|
||||
}
|
||||
|
||||
output.category = [CATEGORY];
|
||||
output.url = [{
|
||||
type: ['source'],
|
||||
value: getContactURI(output)
|
||||
}];
|
||||
|
||||
return output;
|
||||
};
|
||||
|
||||
var getContactURI = function getContactURI(contact) {
|
||||
return URN_IDENTIFIER + contact.uid;
|
||||
};
|
||||
|
||||
// This will be a full url like:
|
||||
// http://www.google.com/m8/feeds/contacts/<email>/base/<contact_id>
|
||||
// for a specific contact node
|
||||
var getUid = function getUid(contact) {
|
||||
return contact.querySelector('id').textContent;
|
||||
};
|
||||
|
||||
// Returns an array with the possible emails found in a contact
|
||||
// as a ContactField format
|
||||
var parseEmails = function parseEmails(googleContact) {
|
||||
var DEFAULT_EMAIL_TYPE = 'other';
|
||||
var emails = [];
|
||||
var fields = googleContact.getElementsByTagNameNS(GD_NAMESPACE,
|
||||
'email');
|
||||
if (fields && fields.length > 0) {
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
var emailField = fields.item(i);
|
||||
|
||||
// Type format: rel="http://schemas.google.com/g/2005#home"
|
||||
var type = emailField.getAttribute('rel') || DEFAULT_EMAIL_TYPE;
|
||||
if (type.indexOf('#') > -1) {
|
||||
type = type.substr(type.indexOf('#') + 1);
|
||||
}
|
||||
|
||||
emails.push({
|
||||
'type': [type],
|
||||
'value': emailField.getAttribute('address')
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return emails;
|
||||
};
|
||||
|
||||
// Given a google contact returns an array of ContactAddress
|
||||
var parseAddress = function parseAddress(googleContact) {
|
||||
var addresses = [];
|
||||
var fields = googleContact.getElementsByTagNameNS(GD_NAMESPACE,
|
||||
'structuredPostalAddress');
|
||||
if (fields && fields.length > 0) {
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
var field = fields.item(i);
|
||||
var address = {};
|
||||
|
||||
address.streetAddress = getValueForNode(field, 'street');
|
||||
address.locality = getValueForNode(field, 'city');
|
||||
address.region = getValueForNode(field, 'region');
|
||||
address.postalCode = getValueForNode(field, 'postcode');
|
||||
address.countryName = getValueForNode(field, 'country');
|
||||
|
||||
addresses.push(address);
|
||||
}
|
||||
}
|
||||
return addresses;
|
||||
};
|
||||
|
||||
// Given a google contact this function returns an array of
|
||||
// ContactField with the pones stored for that contact
|
||||
var parsePhones = function parsePhones(googleContact) {
|
||||
var DEFAULT_PHONE_TYPE = 'other';
|
||||
var GMAIL_MAP = {
|
||||
'work_fax' : 'faxOffice',
|
||||
'home_fax' : 'faxHome',
|
||||
'pager' : 'other',
|
||||
'main' : 'other'
|
||||
};
|
||||
var phones = [];
|
||||
var fields = googleContact.getElementsByTagNameNS(GD_NAMESPACE,
|
||||
'phoneNumber');
|
||||
if (fields && fields.length > 0) {
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
var field = fields.item(i);
|
||||
|
||||
// Type format: rel="http://schemas.google.com/g/2005#home"
|
||||
var type = field.getAttribute('rel') || DEFAULT_PHONE_TYPE;
|
||||
if (type.indexOf('#') > -1) {
|
||||
type = type.substr(type.indexOf('#') + 1);
|
||||
}
|
||||
|
||||
phones.push({
|
||||
'type': [GMAIL_MAP[type] || type],
|
||||
'value': field.textContent
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return phones;
|
||||
};
|
||||
|
||||
// Given a contact from the mozcontact api, fetch the Google Contacts
|
||||
// identifier
|
||||
var getContactUid = function getContactUid(deviceContact) {
|
||||
var out = -1;
|
||||
|
||||
var url = deviceContact.url;
|
||||
if (Array.isArray(url)) {
|
||||
var targetUrls = url.filter(function(aUrl) {
|
||||
return Array.isArray(aUrl.type) &&
|
||||
aUrl.type.indexOf('source') !== -1 &&
|
||||
aUrl.value;
|
||||
});
|
||||
|
||||
if (targetUrls[0]) {
|
||||
out = resolveURI(targetUrls[0].value);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
// From a contact URL, that we expect to be a URI
|
||||
// return the google contact id if we find it on
|
||||
// the uri, -1 otherwise
|
||||
var resolveURI = function resolveURI(uri) {
|
||||
if (uri && uri.indexOf(URN_IDENTIFIER) === 0) {
|
||||
var output = uri.substr(URN_IDENTIFIER.length);
|
||||
if (output && output.length > 0) {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
||||
var downloadContactPicture = function downloadContactPicture(googleContact,
|
||||
access_token, callbacks) {
|
||||
var url = buildContactPhotoURL(googleContact, access_token);
|
||||
return Rest.get(url, callbacks, {
|
||||
'responseType': 'blob'
|
||||
});
|
||||
};
|
||||
|
||||
// Build the url of the photo with the access token
|
||||
var buildContactPhotoURL = function contactPhotoURL(contact, access_token) {
|
||||
if (photoUrls && photoUrls[contact.uid]) {
|
||||
return photoUrls[contact.uid] + '?access_token=' + access_token;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
var startSync = function startSync() {
|
||||
//We don't sync Google contacts (yet)
|
||||
};
|
||||
|
||||
var getServiceName = (function getServiceName() {
|
||||
return 'gmail';
|
||||
})();
|
||||
|
||||
var getAutomaticLogout = (function getAutomaticLogout() {
|
||||
return true;
|
||||
})();
|
||||
|
||||
return {
|
||||
'listAllContacts': listAllContacts,
|
||||
'listDeviceContacts': listDeviceContacts,
|
||||
'getImporter': getImporter,
|
||||
'cleanContacts': cleanContacts,
|
||||
'adaptDataForShowing': adaptDataForShowing,
|
||||
'adaptDataForSaving': adaptDataForSaving,
|
||||
'getContactUid': getContactUid,
|
||||
'downloadContactPicture': downloadContactPicture,
|
||||
'startSync': startSync,
|
||||
'name': getServiceName,
|
||||
'automaticLogout': getAutomaticLogout,
|
||||
'gContactToJson': gContactToJson
|
||||
};
|
||||
|
||||
})();
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
;'use strict';(function(document){var params=parseParams(window.location.search.substring(1));var serviceName=getServiceName();var allowedOrigin=location.origin;function parseParams(paramsStr){var out={};var paramsList;if(paramsStr.indexOf('&')!=-1){paramsList=paramsStr.split('&');}
|
||||
else{paramsList=[paramsStr];}
|
||||
paramsList.forEach(function(param){var paramsValues=param.split('=');out[paramsValues[0]]=paramsValues[1];});return out;}
|
||||
function getServiceName(){return params.service;}
|
||||
var servicesConnectors={'live':function(cb){LazyLoader.load('/shared/js/contacts/import/live/live_connector.js',function(){cb(LiveConnector);});},'gmail':function gmailLoader(cb){LazyLoader.load('/shared/js/contacts/import/gmail/gmail_connector.js',function onLoad(){cb(GmailConnector);});},'facebook':function(cb){var files=['/shared/js/fb/fb_request.js','/shared/js/contacts/import/facebook/fb_data.js','/shared/js/contacts/import/facebook/fb_utils.js','/shared/js/fb/fb_reader_utils.js','/shared/js/contacts/import/facebook/fb_contact_utils.js','/shared/js/contacts/import/facebook/fb_query.js','/shared/js/contacts/import/facebook/fb_contact.js','/shared/js/contacts/import/facebook/facebook_connector.js','/shared/pages/import/style/facebook.css'];if(!params.ftu){files.push('/facebook/js/fb_sync.js');}
|
||||
LazyLoader.load(files,function(){cb(FacebookConnector);});}};function getServiceConnector(cb){if(serviceName){servicesConnectors[serviceName](cb);}
|
||||
else{throw new Error('Service Connector not found !!!');}}
|
||||
function tokenReady(access_token){if(document.readyState==='complete'){onLoad(access_token);}
|
||||
else{window.addEventListener('load',function do_load(){onLoad(access_token);window.removeEventListener('load',do_load);});}}
|
||||
function onLoad(access_token){utils.listeners.add({'#import-action':importer.ui.importAll,'#done-search':window.Search.exitSearchMode,'#groups-list':importer.ui.selection,'#header':[{event:'action',handler:importer.ui.end}],'#search-start':[{event:'click',handler:window.Search.enterSearchMode}]});document.querySelector('#select-all').onclick=importer.ui.selectAll;document.querySelector('#deselect-all').onclick=importer.ui.unSelectAll;importer.ui.init();getServiceConnector(function(connector){importer.start(access_token,connector,allowedOrigin);});}
|
||||
window.addEventListener('message',function getAccessToken(e){if(e.origin!==allowedOrigin){return;}
|
||||
window.removeEventListener('message',getAccessToken);if(e.data.type==='token'){tokenReady(e.data.data);}});parent.postMessage({type:'messaging_ready',data:''},allowedOrigin);})(document);
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
;;;'use strict';if(!window.ImportStatusData){var ImportStatusData=(function(){var store=null;var DS_NAME='Import_Status_Data';var datastoreLoading=false;var datastoreLoaded=false;var DS_LOADED_EVENT='datastore_loaded';var getDatastore=function getDatastore(){return new Promise(function(resolve,reject){if(!navigator.getDataStores){reject({name:'DatastoreNotEnabled'});return;}
|
||||
if(datastoreLoaded){resolve(store);return;}
|
||||
if(datastoreLoading){document.addEventListener(DS_LOADED_EVENT,function loadedHandler(){document.removeEventListener(DS_LOADED_EVENT,loadedHandler);resolve(store);});}
|
||||
datastoreLoading=true;navigator.getDataStores(DS_NAME).then(function(stores){store=stores[0];},reject).then(function(){datastoreLoading=false;datastoreLoaded=true;document.dispatchEvent(new CustomEvent(DS_LOADED_EVENT));resolve(store);},reject);});};var put=function put(key,obj){return new Promise(function(resolve,reject){getDatastore().then(function success(store){store.put(obj,key).then(resolve,reject);},reject);});};var remove=function remove(key){return new Promise(function(resolve,reject){getDatastore().then(function success(store){store.remove(key).then(resolve,reject);},reject);});};var get=function get(key){return new Promise(function(resolve,reject){getDatastore().then(function success(store){store.get(key).then(resolve,reject);},reject);});};var clear=function clear(){return new Promise(function(resolve,reject){getDatastore().then(function success(store){store.clear().then(resolve,reject);},reject);});};return{put:put,remove:remove,get:get,clear:clear};})();}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
;'use strict';if(typeof window.importer==='undefined'){(function(document){var Importer=window.importer={};var UI=Importer.ui={};var TOKEN_EXPIRED_STR='request_token_expired';var TOKEN_EXPIRED_CODE=190;var serviceConnector;var targetApp;var access_token;var startCallback;var selectedContacts={};var unSelectedContacts={};var selectableFriends={};var myFriends,myFriendsByUid;var checked=0;var checkNodeList;var existingContacts=[];var existingContactsByUid={};var contactsLoaded=false,friendsLoaded=false;var currentRequest;var currentNetworkRequest=null;var cancelled=false;var friendsImported;var syncOngoing=false;var updateButton,selectAllButton,deSelectAllButton,contactList,headerElement,friendsMsgElement,scrollableElement;var imgLoader;var HARD_LIMIT_SYNC=300;var tokenKey;var isOnLine=navigator.onLine;var ongoingImport=false;var ongoingClean=false;var theImporter,theCleaner;window.addEventListener('online',onLineChanged);window.addEventListener('offline',onLineChanged);function notifyParent(message,origin){parent.postMessage({type:message.type||'',data:message.data||'',messageId:message.messageId,additionalMessageId:message.additionalMessageId},origin);}
|
||||
function showOfflineDialog(yesCb,noCb){var recommend=serviceConnector.name==='facebook';var dialog=parent.document.getElementById('confirmation-message');parent.LazyLoader.load(dialog,function(){LazyLoader.load('/shared/js/confirm.js',function(){ConfirmDialog.show('connectionLost','connectionLostMsg',{title:'noOption',isRecommend:!recommend,callback:function(){ConfirmDialog.hide();noCb();}},{title:'yesOption',isRecommend:recommend,callback:function(){ConfirmDialog.hide();yesCb();}},{zIndex:'10000'});});});}
|
||||
function onLineChanged(){isOnLine=navigator.onLine;if(isOnLine===false&&ongoingImport){theImporter.hold();showOfflineDialog(function(){theImporter.resume();},function(){theImporter.finish();});}}
|
||||
function getTokenKey(){var key='tokenData';var serviceName=serviceConnector.name;if(serviceName!=='facebook'){key+=('_'+serviceName);}
|
||||
return key;}
|
||||
var searchSource={getNodes:function(){return contactList.querySelectorAll('section > ol > li');},getFirstNode:function(){return contactList.querySelector('section > ol > li');},getNextNode:function(contact){var out=contact.nextElementSibling;var nextParent=contact.parentNode.parentNode.nextElementSibling;while(!out&&nextParent){out=nextParent.querySelector('ol > li:first-child');nextParent=nextParent.nextElementSibling;}
|
||||
return out;},expectMoreNodes:function(){return false;},clone:function(node){return node.cloneNode(true);},getNodeById:function(id){return contactsList.querySelector('[data-uuid="'+id+'"]');},getSearchText:function(node){return node.dataset.search;},click:onSearchResultCb};UI.init=function(){var overlay=document.querySelector('nav[data-type="scrollbar"] p');var jumper=document.querySelector('nav[data-type="scrollbar"] ol');updateButton=document.getElementById('import-action');selectAllButton=document.querySelector('#select-all');deSelectAllButton=document.querySelector('#deselect-all');contactList=document.querySelector('#groups-list');headerElement=document.querySelector('header');friendsMsgElement=document.querySelector('#friends-msg');scrollableElement=document.querySelector('#mainContent');var params={overlay:overlay,jumper:jumper,groupSelector:'#group-',scrollToCb:scrollToCb};utils.alphaScroll.init(params);window.Search.init(searchSource,true);};function notifyLogout(){var msg={type:'logout_finished',data:''};parent.postMessage(msg,targetApp);}
|
||||
UI.end=function(event){notifyParent({type:'window_close'},targetApp);notifyLogout();};function removeToken(cb){var theCb=(typeof cb==='function')?cb:function(){};ImportStatusData.remove(tokenKey).then(theCb,theCb);}
|
||||
function markPendingLogout(url,service,cb){var PENDING_LOGOUT_KEY='pendingLogout';window.asyncStorage.getItem(PENDING_LOGOUT_KEY,function(data){var obj=data||{};obj[service]=url;var theCb=(typeof cb==='function')?cb:function(){};window.asyncStorage.setItem(PENDING_LOGOUT_KEY,obj,theCb,theCb);});}
|
||||
function serviceLogout(cb){if(serviceConnector.automaticLogout){var serviceName=serviceConnector.name;var logoutUrl=oauthflow.params[serviceName].logoutUrl;var callbacks={error:function(){window.console.warn('Error while logging out user ',logoutUrl);removeToken();markPendingLogout(logoutUrl,serviceName,cb);},timeout:function(){window.console.warn('Timeout while logging out user ',logoutUrl);removeToken();markPendingLogout(logoutUrl,serviceName,cb);},success:function(){window.console.log('Successfully logged out');removeToken(cb);}};if(navigator.onLine===true){Rest.get(logoutUrl,callbacks);}
|
||||
else{removeToken();markPendingLogout(logoutUrl,serviceName,cb);}}
|
||||
else{cb();}}
|
||||
function tokenExpired(error){return(error.code===TOKEN_EXPIRED_CODE||error===TOKEN_EXPIRED_STR);}
|
||||
function scrollToCb(groupContainer){scrollableElement.scrollTop=groupContainer.offsetTop;}
|
||||
function onSearchResultCb(e){var target=e.target;var uid=target.dataset.uuid;var checkbox=target.querySelector('input[type="checkbox"]');checkbox.checked=!checkbox.checked;var realNode=contactList.querySelector('[data-uuid='+'"'+uid+'"'+']');UI.selection({target:realNode});}
|
||||
Importer.reset=function(){selectedContacts={};unSelectedContacts={};selectableFriends={};myFriends=[];myFriendsByUid={};checked=0;existingContacts=[];existingContactsByUid={};contactsLoaded=false;friendsLoaded=false;currentRequest=null;currentNetworkRequest=null;friendsImported=false;syncOngoing=false;ongoingImport=ongoingClean=false;selectAllButton=document.getElementById('select-all');deSelectAllButton=document.getElementById('deselect-all');};Importer.start=function(acc_tk,connector,ptargetApp,startCb){startCallback=startCb;var serviceName=connector.name;document.querySelector('#content h1').setAttribute('data-l10n-id',serviceName+'-serviceName');document.body.classList.add(serviceName);serviceConnector=connector;targetApp=ptargetApp;tokenKey=getTokenKey();setCurtainHandlers();if(acc_tk){access_token=acc_tk;Importer.getFriends(acc_tk);}
|
||||
else{oauth2.getAccessToken(function(new_acc_tk){access_token=new_acc_tk;Importer.getFriends(new_acc_tk);},'friends',serviceConnector.name);}};function contactsReady(result){existingContacts=result;contactsLoaded=true;if(friendsLoaded){markExisting(existingContacts);}}
|
||||
function syncSuccess(numChanged){window.console.log('Synchronization ended!!!');syncOngoing=false;serviceConnector.scheduleNextSync();var msg={type:'sync_finished',data:numChanged||''};parent.postMessage(msg,targetApp);}
|
||||
function startSync(){var totalExisting=existingContacts.length;if(totalExisting>0&&totalExisting<HARD_LIMIT_SYNC&&!syncOngoing){syncOngoing=true;serviceConnector.startSync(existingContacts,myFriendsByUid,syncSuccess);}
|
||||
else{var msg={type:'sync_finished',data:0};parent.postMessage(msg,targetApp);}}
|
||||
function friendsAvailable(){imgLoader=new ImageLoader('#mainContent','.block-item:not([data-uuid="#uid#"])');var s='.block-item:not([data-uuid="#uid#"]) input[type="checkbox"]';checkNodeList=contactList.querySelectorAll(s);Array.prototype.slice.call(checkNodeList,0,checkNodeList.length);friendsLoaded=true;if(contactsLoaded){window.addEventListener('message',function importOnViewPort(e){if(e.origin!==targetApp){return;}
|
||||
var data=e.data;if(data&&data.type==='dom_transition_end'){window.removeEventListener('message',importOnViewPort);window.setTimeout(startSync,0);}});markExisting(existingContacts);}
|
||||
Curtain.hide(sendReadyEvent);if(typeof startCallback==='function'){window.setTimeout(startCallback,0);}}
|
||||
function sendReadyEvent(){parent.postMessage({type:'ready',data:''},targetApp);}
|
||||
function markExisting(deviceFriends){updateButton.setAttribute('data-l10n-id',deviceFriends.length===0?'import':'update');var reallyExisting=0;deviceFriends.forEach(function(fbContact){var uid=serviceConnector.getContactUid(fbContact);if(myFriendsByUid[uid]){reallyExisting++;}
|
||||
delete selectableFriends[uid];var ele=document.querySelector('[data-uuid="'+uid+'"]');if(ele){setChecked(ele.querySelector('input[type="checkbox"]'),true);}
|
||||
if(existingContactsByUid[uid]){existingContactsByUid[uid].push(fbContact);}else{existingContactsByUid[uid]=[fbContact];}});if(myFriends.length<1){friendsMsgElement.setAttribute('data-l10n-id','fbNoFriends');}else{var newValue=myFriends.length-
|
||||
Object.keys(existingContactsByUid).length;navigator.mozL10n.setAttributes(friendsMsgElement,'fbFriendsFound',{numFriends:newValue<0?0:newValue});}
|
||||
checkDisabledButtons();}
|
||||
Importer.setSelected=function(friends){selectedContacts=friends;};function setCurtainHandlers(){Curtain.oncancel=cancelCb;}
|
||||
Importer.getFriends=function(acc_tk){currentNetworkRequest=serviceConnector.listAllContacts(acc_tk,{success:importer.friendsReady,error:importer.errorHandler,timeout:importer.timeoutHandler});if(!navigator.mozContacts){return;}
|
||||
var callbacks={success:contactsReady,error:function(errorName){window.console.error('Error while retrieving existing dev contacts: ',errorName);}};serviceConnector.listDeviceContacts(callbacks);};Importer.friendsReady=function(response){if(typeof response.error==='undefined'){var lmyFriends=response.data;if(lmyFriends.length===0){showNoFriends(function(){Curtain.hide(UI.end);});return;}
|
||||
if(typeof serviceConnector.oncontactsloaded==='function'){serviceConnector.oncontactsloaded(lmyFriends);}
|
||||
myFriendsByUid={};myFriends=[];var totalMyFriends=lmyFriends.length;for(var i=0;i<totalMyFriends;i++){var aFriend=lmyFriends[i];aFriend._idxFriendsArray=i;myFriends.push(serviceConnector.adaptDataForShowing(aFriend));myFriendsByUid[aFriend.uid]=aFriend;selectableFriends[aFriend.uid]=aFriend;}
|
||||
asyncStorage.getItem('order.lastname',function orderValue(lastName){var options={container:'#groups-list',orderBy:lastName?'lastName':'firstName'};FriendListRenderer.render(myFriends,friendsAvailable,options);});}
|
||||
else{window.console.error('Error, while retrieving friends',response.error.message);if(!tokenExpired(response.error)){setCurtainHandlersErrorFriends();Curtain.show('error','friends');}
|
||||
else{window.console.warn('Access Token expired or revoked');Curtain.hide(notifyParent.bind(null,{type:'token_error'},targetApp));ImportStatusData.remove(tokenKey).then(function token_removed(){oauth2.getAccessToken(function(new_acc_tk){access_token=new_acc_tk;Importer.getFriends(new_acc_tk);},'friends',serviceConnector.name);});}}};function showNoFriends(callback){var dialog=parent.document.getElementById('confirmation-message');parent.LazyLoader.load(dialog,function(){LazyLoader.load('/shared/js/confirm.js',function(){ConfirmDialog.show(null,'emptyAccount',{title:'ok',isRecommend:true,callback:function(){ConfirmDialog.hide();if(typeof callback==='function'){callback();}}},null,{zIndex:'10000'});if(typeof startCallback==='function'){window.setTimeout(startCallback,0);}});});}
|
||||
function cancelImport(){cancelled=true;var cancelFunc=onUpdate;if(ongoingImport){cancelFunc=theImporter.finish;}else if(ongoingClean){cancelFunc=theCleaner.finish;}
|
||||
cancelFunc();Curtain.show('message','canceling');}
|
||||
function cancelCb(){if(currentNetworkRequest){currentNetworkRequest.cancel();currentNetworkRequest=null;}
|
||||
Curtain.hide(notifyParent.bind(null,{type:'abort'},targetApp));}
|
||||
function setCurtainHandlersErrorFriends(){Curtain.oncancel=function friends_cancel(){Curtain.hide(notifyParent.bind(null,{type:'abort'},targetApp));};Curtain.onretry=function get_friends(){Curtain.oncancel=cancelCb;Curtain.show('wait','friends');Importer.getFriends(access_token);};}
|
||||
function checkUpdateButton(){if(Object.keys(selectedContacts).length>0||Object.keys(unSelectedContacts).length>0){updateButton.disabled=false;}else{updateButton.disabled=true;}}
|
||||
function checkDisabledButtons(){checkUpdateButton();switch(checked){case 0:deSelectAllButton.disabled=true;selectAllButton.disabled=false;break;case myFriends.length:selectAllButton.disabled=true;deSelectAllButton.disabled=false;break;default:deSelectAllButton.disabled=false;selectAllButton.disabled=false;break;}}
|
||||
function setChecked(element,value){if(element.checked!==value){value?++checked:--checked;}
|
||||
element.checked=value;}
|
||||
Importer.baseHandler=function(type){setCurtainHandlersErrorFriends();Curtain.show(type,'friends');};Importer.timeoutHandler=function(){Importer.baseHandler('timeout');};Importer.errorHandler=function(){Importer.baseHandler('error');};function onUpdate(numFriends,numMergedDuplicated){serviceLogout(notifyLogout);if(Importer.getContext()==='ftu'){Curtain.hide(notifyParent.bind(null,{type:'window_close',messageId:cancelled?null:{id:'friendsUpdated',args:{numFriends:numFriends}},additionalMessageId:(cancelled||!numMergedDuplicated)?null:{id:'friendsMerged',args:{numDups:numMergedDuplicated}}},targetApp));}else{notifyParent({type:'import_updated'},targetApp);window.addEventListener('message',function finished(e){if(e.origin!==targetApp){return;}
|
||||
if(e.data.type==='contacts_loaded'){var messageId=cancelled?null:{id:'friendsUpdated',args:{numFriends:numFriends}};var additionalMessageId=(cancelled||!numMergedDuplicated)?null:{id:'friendsMerged',args:{numDups:numMergedDuplicated}};Curtain.hide(notifyParent.bind(null,{type:'window_close',messageId:messageId,additionalMessageId:additionalMessageId},targetApp));window.removeEventListener('message',finished);}});}}
|
||||
function cleanContacts(onsuccess,progress){if(cancelled){return;}
|
||||
var contacts=[];var unSelectedKeys=Object.keys(unSelectedContacts);unSelectedKeys.forEach(function iterator(uid){var deviceContacts=unSelectedContacts[uid];for(var i=0;i<deviceContacts.length;i++){contacts.push(deviceContacts[i]);}});var mode='update';if(unSelectedKeys.length===existingContacts.length&&Object.keys(selectedContacts).length===0){mode='clear';Curtain.hideMenu();}
|
||||
serviceConnector.cleanContacts(contacts,mode,function gotCleaner(cleaner){if(cleaner){theCleaner=cleaner;ongoingClean=true;cleaner.oncleaned=progress.update;cleaner.onsuccess=function(){ongoingClean=false;theCleaner=null;onsuccess();};}
|
||||
else{Importer.errorHandler();}});}
|
||||
function getTotalUnselected(){var total=0;Object.keys(unSelectedContacts).forEach(function surfing(uid){total=total+unSelectedContacts[uid].length;});return total;}
|
||||
UI.importAll=function(e){imgLoader.unload();var selected=Object.keys(selectedContacts).length;var unSelected=getTotalUnselected();var total=selected+unSelected;cancelled=false;var progress;if(selected>0){progress=Curtain.show('progress','import');progress.setTotal(total);Curtain.oncancel=cancelImport;Importer.importAll(function on_all_imported(totalImported,numDups){if(typeof serviceConnector.oncontactsimported==='function'){serviceConnector.oncontactsimported(existingContacts,friendsImported,function(){friendsImported=true;});}
|
||||
if(!cancelled&&unSelected>0){progress.setFrom('update');cleanContacts(function callback(){onUpdate(progress.getValue(),numDups);},progress);}else{onUpdate(progress.getValue(),numDups);}},progress);}else if(unSelected>0){progress=Curtain.show('progress','update');progress.setTotal(total);Curtain.oncancel=cancelImport;cleanContacts(function callback(){onUpdate(progress.getValue());},progress);}};UI.selectAll=function(e){deSelectAllButton.disabled=false;selectAllButton.disabled=true;window.setTimeout(function doSelectAll(){bulkSelection(true);unSelectedContacts={};selectedContacts={};for(var uid in selectableFriends){selectedContacts[uid]=selectableFriends[uid];}
|
||||
checkUpdateButton();},0);return false;};UI.unSelectAll=function(e){deSelectAllButton.disabled=true;selectAllButton.disabled=false;window.setTimeout(function doUnSelectAll(){bulkSelection(false);selectedContacts={};unSelectedContacts={};for(var uid in existingContactsByUid){unSelectedContacts[uid]=existingContactsByUid[uid];}
|
||||
checkUpdateButton();},0);return false;};function bulkSelection(value){window.setTimeout(function(){doSelect(value,checkNodeList,0,10);},0);}
|
||||
function doSelect(value,list,from,chunkSize){var total=list.length;for(var j=from;j<from+chunkSize&&j<total;j++){setChecked(list[j],value);}
|
||||
var leftInterval=from+chunkSize;var rightInterval=leftInterval+chunkSize<total?leftInterval+chunkSize:total;window.setTimeout(function(){doSelect(value,list,leftInterval,rightInterval);},0);}
|
||||
UI.selection=function(e){var out=false;var target=e.target;if(target&&target.dataset.uuid){var uuid=target.dataset.uuid;var checkbox=target.querySelector('input[type="checkbox"]');setChecked(checkbox,!checkbox.checked);if(checkbox.checked===true){if(unSelectedContacts[uuid]){delete unSelectedContacts[uuid];}else{selectedContacts[uuid]=myFriendsByUid[uuid];}}else{delete selectedContacts[uuid];if(existingContactsByUid[uuid]){unSelectedContacts[uuid]=existingContactsByUid[uuid];}}
|
||||
checkDisabledButtons();out=true;}
|
||||
return out;};Importer.getContext=function(){var out='contacts';if(window.location.search.indexOf('ftu')!==-1){out='ftu';}
|
||||
return out;};function releaseObjs(cfdata){if(!cfdata){return;}
|
||||
selectedContacts[cfdata.uid]=null;myFriendsByUid[cfdata.uid]=null;selectableFriends[cfdata.uid]=null;myFriends[cfdata._idxFriendsArray]=null;if(cfdata.fbInfo&&Array.isArray(cfdata.fbInfo.photo)){cfdata.fbInfo.photo=null;}}
|
||||
function doImportAll(importedCB,progress){if(cancelled){return;}
|
||||
checkNodeList=null;theImporter=serviceConnector.getImporter(selectedContacts,access_token);var cpuLock,screenLock;theImporter.oncontactimported=function(cfdata){releaseObjs(cfdata);progress.update();};theImporter.onsuccess=function(totalImported,totalMerged){ongoingImport=false;window.setTimeout(function imported(){utils.misc.setTimestamp(serviceConnector.name,()=>{importedCB(totalImported,totalMerged);});},0);if(cpuLock){cpuLock.unlock();}
|
||||
if(screenLock){screenLock.unlock();}};cpuLock=navigator.requestWakeLock('cpu');screenLock=navigator.requestWakeLock('screen');document.querySelector('#groups-list').innerHTML='';ongoingImport=true;theImporter.start();}
|
||||
Importer.importAll=function(importedCB,progress){if(isOnLine===true){doImportAll(importedCB,progress);}
|
||||
else{window.console.warn('User is not online!!');showOfflineDialog(function(){doImportAll(importedCB,progress);},function(){Curtain.hide(UI.end);});}};})(document);}
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
'use strict';
|
||||
|
||||
/* global Rest */
|
||||
/* global utils */
|
||||
|
||||
if (!window.LiveConnector) {
|
||||
window.LiveConnector = (function() {
|
||||
var LIVE_ENDPOINT = 'https://apis.live.net/v5.0/';
|
||||
var CONTACTS_RESOURCE = 'me/contacts';
|
||||
var PICTURE_RESOURCE = '/picture';
|
||||
|
||||
var LIVE_CATEGORY = 'live';
|
||||
|
||||
var itemsTypeMap = {
|
||||
'personal': 'personal',
|
||||
'mobile': 'mobile',
|
||||
'business': 'work',
|
||||
'other': 'other',
|
||||
'preferred': 'personal'
|
||||
};
|
||||
|
||||
function fillAddress(addrType, addrValue) {
|
||||
var out = {
|
||||
type: [addrType],
|
||||
streetAddress: addrValue.street || '',
|
||||
locality: addrValue.city || '',
|
||||
region: addrValue.state || '',
|
||||
countryName: addrValue.region || '',
|
||||
postalCode: addrValue.postal_code || ''
|
||||
};
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
function getURI(liveContact) {
|
||||
return 'urn:uuid:' + (liveContact.user_id || liveContact.id);
|
||||
}
|
||||
|
||||
function resolveURI(uri) {
|
||||
var components = uri.split(':');
|
||||
// The third element is the user id of the live contact
|
||||
return components[2];
|
||||
}
|
||||
|
||||
function LiveConnector() {
|
||||
}
|
||||
|
||||
LiveConnector.prototype = {
|
||||
listAllContacts: function(access_token, callbacks) {
|
||||
var uriElements = [LIVE_ENDPOINT, CONTACTS_RESOURCE, '?',
|
||||
'access_token', '=', access_token];
|
||||
|
||||
return Rest.get(uriElements.join(''), callbacks);
|
||||
},
|
||||
|
||||
listDeviceContacts: function(callbacks) {
|
||||
var filterOptions = {
|
||||
filterValue: LIVE_CATEGORY,
|
||||
filterOp: 'contains',
|
||||
filterBy: ['category']
|
||||
};
|
||||
var req = navigator.mozContacts.find(filterOptions);
|
||||
req.onsuccess = function() {
|
||||
callbacks.success(req.result);
|
||||
};
|
||||
req.onerror = function() {
|
||||
callbacks.error(req.error);
|
||||
};
|
||||
},
|
||||
|
||||
getImporter: function(contactsList, access_token) {
|
||||
return new window.ContactsImporter(contactsList, access_token, this);
|
||||
},
|
||||
|
||||
cleanContacts: function(contactsList, mode, cb) {
|
||||
// Just a placeholder for the moment
|
||||
var cleaner = new window.ContactsCleaner(contactsList);
|
||||
window.setTimeout(cleaner.start, 0);
|
||||
cb(cleaner);
|
||||
},
|
||||
|
||||
adaptDataForShowing: function(source) {
|
||||
var out = source;
|
||||
|
||||
out.uid = source.user_id || source.id;
|
||||
out.givenName = [source.first_name || ''];
|
||||
out.familyName = [source.last_name || ''];
|
||||
out.email1 = source.emails.account ||
|
||||
source.emails.preferred ||
|
||||
source.emails.personal ||
|
||||
'';
|
||||
|
||||
out.contactPictureUri = [LIVE_ENDPOINT, out.uid,
|
||||
PICTURE_RESOURCE, '?type=medium'].join('');
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
adaptDataForSaving: function live2MozContact(liveContact) {
|
||||
var out = {
|
||||
givenName: [liveContact.first_name || ''],
|
||||
familyName: [liveContact.last_name || ''],
|
||||
name: [liveContact.name || ''],
|
||||
tel: [],
|
||||
email: [],
|
||||
adr: [],
|
||||
photo: liveContact.photo,
|
||||
category: [LIVE_CATEGORY],
|
||||
url: [{
|
||||
type: ['source'],
|
||||
value: getURI(liveContact)
|
||||
}]
|
||||
};
|
||||
|
||||
// Months in JS goes from 0 to 11 :(
|
||||
var bmonth = liveContact.birth_month - 1;
|
||||
var bday = liveContact.birth_day;
|
||||
if (bmonth > -1 && bday) {
|
||||
var birthdate = out.bday = new Date();
|
||||
birthdate.setUTCDate(bday);
|
||||
birthdate.setUTCMonth(bmonth, bday);
|
||||
birthdate.setUTCFullYear(utils.misc.FLAG_YEAR_IGNORED);
|
||||
}
|
||||
|
||||
var liveEmails = liveContact.emails || {};
|
||||
var alreadyAddedEmails = {};
|
||||
Object.keys(liveEmails).forEach(function(emailType) {
|
||||
var emailValue = liveEmails[emailType];
|
||||
var present = (typeof alreadyAddedEmails[emailValue] === 'boolean');
|
||||
if (emailValue && !present) {
|
||||
out.email.push({
|
||||
type: [itemsTypeMap[emailType]],
|
||||
value: emailValue
|
||||
});
|
||||
alreadyAddedEmails[emailValue] = true;
|
||||
}
|
||||
});
|
||||
|
||||
var livePhones = liveContact.phones || {};
|
||||
Object.keys(livePhones).forEach(function(phoneType) {
|
||||
var phoneValue = livePhones[phoneType];
|
||||
if (phoneValue) {
|
||||
out.tel.push({
|
||||
type: [itemsTypeMap[phoneType]],
|
||||
value: phoneValue
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var liveAddresses = liveContact.addresses || {};
|
||||
Object.keys(liveAddresses).forEach(function(addrType) {
|
||||
var addrValue = liveAddresses[addrType];
|
||||
if (addrValue) {
|
||||
out.adr.push(fillAddress(itemsTypeMap[addrType], addrValue));
|
||||
}
|
||||
});
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
// It would allow to know the UID of a service contact already imported
|
||||
// on the device. That is needed by the generic importer, for live
|
||||
// a dummy implementation as we are currently not supporting updates
|
||||
getContactUid: function(deviceContact) {
|
||||
var out = '-1';
|
||||
|
||||
var url = deviceContact.url;
|
||||
if (Array.isArray(url)) {
|
||||
var targetUrls = url.filter(function(aUrl) {
|
||||
return Array.isArray(aUrl.type) &&
|
||||
aUrl.type.indexOf('source') !== -1;
|
||||
});
|
||||
if (targetUrls[0]) {
|
||||
out = resolveURI(targetUrls[0].value);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
get name() {
|
||||
return 'live';
|
||||
},
|
||||
|
||||
get automaticLogout() {
|
||||
return true;
|
||||
},
|
||||
|
||||
downloadContactPicture: function(contact, access_token, callbacks) {
|
||||
if (contact.user_id) {
|
||||
var uriElements = [LIVE_ENDPOINT, contact.user_id, PICTURE_RESOURCE,
|
||||
'?', 'access_token', '=', access_token];
|
||||
|
||||
return Rest.get(uriElements.join(''), callbacks, {
|
||||
responseType: 'blob'
|
||||
});
|
||||
}
|
||||
else {
|
||||
callbacks.success(null);
|
||||
}
|
||||
},
|
||||
|
||||
startSync: function() {
|
||||
// Sync not supported
|
||||
}
|
||||
};
|
||||
|
||||
return new LiveConnector();
|
||||
})();
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
;'use strict';var utils=window.utils||{};if(typeof utils.config==='undefined'){(function(){var configs={};var initializing={};var initialized={};var EVENT_INITIALIZED='config_initialized';utils.config=utils.config||{};utils.config.reset=function(){configs={};initializing={};initialized={};};function onLoad(xhr,file,resolveCb){configs[file]=xhr.response;delete initializing[file];initialized[file]=true;document.dispatchEvent(new CustomEvent(EVENT_INITIALIZED,{detail:{file:file,success:true}}));resolveCb(configs[file]);}
|
||||
utils.config.load=function(file){return new Promise(function(resolve,reject){var isInitialized=initialized[file];if(isInitialized===true){resolve(configs[file]);return;}
|
||||
var isInitializing=initializing[file];if(isInitializing){var handler=function(expectedFile,e){document.removeEventListener(EVENT_INITIALIZED,handler);var file=e.detail.file;if(expectedFile!==file){return;}
|
||||
if(e.detail.success){resolve(configs[file]);}
|
||||
else{reject();}}.bind(null,file);document.addEventListener(EVENT_INITIALIZED,handler);return;}
|
||||
initializing[file]=true;var xhr=new XMLHttpRequest();xhr.open('GET',file,true);xhr.responseType='json';xhr.onload=function(){var resolvedCb=resolve;if(xhr.status!==200){resolvedCb=reject;}
|
||||
onLoad(xhr,file,resolvedCb);};xhr.onerror=function(){document.dispatchEvent(new CustomEvent(EVENT_INITIALIZED,{detail:{file:file,success:false}}));delete initializing[file];reject(xhr.error);};try{xhr.send(null);}
|
||||
catch(e){console.error('Error while loading config file: ',e.message);onLoad({status:404},file,reject);}});};})();}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
;'use strict';var utils=window.utils||{};utils.importFromVcard=function(file,callback){var MERGE_DEPENDENCIES=['/shared/js/simple_phone_matcher.js','/shared/js/contacts/merger_adapter.js','/shared/js/contacts/contacts_merger.js','/shared/js/contacts/contacts_matcher.js'];Loader.utility('Overlay',function(){LazyLoader.load(MERGE_DEPENDENCIES,_loaded);});function _loaded(){var importedContacts=0;var cancelled=false;var importer=null;var text=null;var progress=utils.overlay.show('memoryCardContacts-reading','activityBar');utils.overlay.showMenu();utils.overlay.oncancel=function oncancel(){cancelled=true;if(importer){importer.finish();}else{utils.overlay.hide();}};readVCard(function(vcardText){if(vcardText){processTextFromFile(vcardText);}else{callback();}});function readVCard(cb){var reader;if(typeof file==='string'){cb(file);}else{reader=new FileReader();reader.onloadend=function(){text=reader.result;if(text){cb(text);}};try{reader.readAsText(file);}
|
||||
catch(ex){console.error('Error reading the file '+ex.message);import_error();cb();}}}
|
||||
function processTextFromFile(textFromFile){if(cancelled){return;}
|
||||
var reader=new VCardReader(textFromFile);var match=textFromFile&&textFromFile.match(/END:VCARD/g);var cursor=reader.getAll();var numContacts=match?match.length:0;var numDupsMerged=0;var firstContact=null;var _doContinue=function(savedContact){if(savedContact){if(!firstContact){firstContact=savedContact;}
|
||||
imported_contact();}
|
||||
if(!cancelled){cursor.continue();}};cursor.onsuccess=function(evt){if(evt.target.result){var contact=evt.target.result;Matcher.match(contact,'passive',{onmatch:function(matches){var callbacks={success:function(mergedContact){numDupsMerged++;_doContinue(mergedContact);},error:_doContinue};contacts.adaptAndMerge(contact,matches,callbacks);},onmismatch:function(){var req=navigator.mozContacts.save(contact);req.onsuccess=function(){_doContinue(contact);};}});}else{utils.overlay.hide();if(!cancelled){var msgImported={id:'memoryCardContacts-imported3',args:{n:importedContacts}};var msgDupsMerged=numDupsMerged?{id:'contactsMerged',args:{numDups:numDupsMerged}}:null;utils.status.show(msgImported,msgDupsMerged);}
|
||||
if(importedContacts>0){callback(importedContacts,firstContact.id);}else{callback(importedContacts);}}};import_read(numContacts);}
|
||||
function import_read(n){progress.setClass('progressBar');progress.setHeaderMsg('memoryCardContacts-importing');progress.setTotal(n);}
|
||||
function imported_contact(){importedContacts++;if(!cancelled){progress.update();}}
|
||||
function import_error(e){console.error('Error importing from vcard: '+e.message);var cancel={title:'cancel',callback:function(){ConfirmDialog.hide();}};var retry={title:'retry',isRecommend:true,callback:function(){ConfirmDialog.hide();processTextFromFile(text);}};ConfirmDialog.show(null,'memoryCardContacts-error',cancel,retry);utils.overlay.hide();}}};
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
;'use strict';function SimContactsImporter(targetIcc){if(targetIcc===null){throw new Error('We need an icc to continue with this operation');}
|
||||
var pointer=0;var CHUNK_SIZE=5;var numResponses=0;var self=this;var mustFinish=false;var loadedMatch=false;var DEFAULT_TEL_TYPE='other';var icc=targetIcc;var iccId=icc.iccInfo&&icc.iccInfo.iccid;var numDupsMerged=0;function generateIccContactUrl(contactid){var urlValue='urn:'+'uuid:'+(iccId||'iccId')+'-'+contactid;return[{type:['source','sim'],value:urlValue}];}
|
||||
function notifyFinish(){if(typeof self.onfinish==='function'){window.setTimeout(self.onfinish,0,numDupsMerged,iccId);}}
|
||||
function notifyImported(){if(typeof self.onimported==='function'){window.setTimeout(self.onimported,0);}}
|
||||
function continueCb(){numResponses++;pointer++;notifyImported();if(pointer<self.items.length&&numResponses===CHUNK_SIZE){numResponses=0;mustFinish?notifyFinish():importSlice(pointer);}
|
||||
else if(pointer>=self.items.length){notifyFinish();}}
|
||||
function startMigration(){if(!mustFinish&&Array.isArray(self.items)&&self.items.length>0){importSlice(0);}
|
||||
else{notifyFinish();}}
|
||||
function onContactsReadyForImport(){if(typeof self.onread==='function'){self.onread(self.items.length);}
|
||||
if(loadedMatch){startMigration();}
|
||||
else{document.addEventListener('matchLoaded',function mloaded(){document.removeEventListener('matchLoaded',mloaded);startMigration();});}}
|
||||
this.start=function(){numDupsMerged=0;if(mustFinish){notifyFinish();return;}
|
||||
LazyLoader.load(['/shared/js/simple_phone_matcher.js','/shared/js/contacts/contacts_matcher.js','/shared/js/contacts/contacts_merger.js','/shared/js/contacts/utilities/image_thumbnail.js','/shared/js/contacts/merger_adapter.js'],function loaded(){loadedMatch=true;document.dispatchEvent(new CustomEvent('matchLoaded'));});var requestAdn,requestSdn;if(icc&&icc.readContacts){requestAdn=icc.readContacts('adn');}else{throw new Error('Not able to obtain a SIM import function from platform');}
|
||||
requestAdn.onsuccess=function onsuccess(){if(mustFinish){notifyFinish();return;}
|
||||
self.items=requestAdn.result||[];requestSdn=icc.readContacts('sdn');requestSdn.onsuccess=function onsuccess(){if(mustFinish){notifyFinish();return;}
|
||||
if(Array.isArray(requestSdn.result)){self.items=self.items.concat(requestSdn.result);}
|
||||
onContactsReadyForImport();};requestSdn.onerror=function error(){if(mustFinish){notifyFinish();return;}
|
||||
console.warn('Could not read SDN Contacts from SIM Card',error.name);onContactsReadyForImport();};};requestAdn.onerror=function error(){if(typeof self.onerror==='function'){self.onerror(requestAdn.error);}};};this.finish=function(){mustFinish=true;};function importSlice(from){for(var i=from;i<from+CHUNK_SIZE&&i<self.items.length;i++){var item=self.items[i];var parsedName=utils.misc.parseName(item.name[0]);item.givenName=[parsedName.givenName];item.familyName=[parsedName.familyName];if(Array.isArray(item.tel)){var telItems=[];for(var j=0;j<item.tel.length;j++){var aTel=item.tel[j];if(aTel.value&&aTel.value.trim()){aTel.type=[DEFAULT_TEL_TYPE];telItems.push(aTel);}}
|
||||
item.tel=telItems;}
|
||||
item.category=['sim'];item.url=generateIccContactUrl(item.id);var contact=item;var cbs={onmatch:function(results){var mergeCbs={success:function(){numDupsMerged++;continueCb();},error:function(e){window.console.error('Error while merging: ',e.name);continueCb();}};contacts.adaptAndMerge(this,results,mergeCbs);}.bind(contact),onmismatch:function(){saveContact(this);}.bind(contact)};Matcher.match(item,'passive',cbs);}}
|
||||
function saveContact(contact){var req=window.navigator.mozContacts.save(new mozContact(contact));req.onsuccess=function saveSuccess(){continueCb();};req.onerror=function saveError(){console.error('SIM Import: Error importing ',contact.id,req.error.name);continueCb();};}}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
;;'use strict';var utils=window.utils||{};var SCALE_RATIO=window.devicePixelRatio||1;var LAST_IMPORT_TIMESTAMP_SUFFIX='_last_import_timestamp';function scale(size){return Math.round(SCALE_RATIO*size);}
|
||||
if(!utils.misc){utils.misc={};utils.misc.toMozContact=function(contact){var outContact=contact;if(!(contact instanceof mozContact)){outContact=new mozContact(contact);outContact.id=contact.id||outContact.id;}
|
||||
return outContact;};const FLAG_YEAR_IGNORED=9996;utils.misc.FLAG_YEAR_IGNORED=FLAG_YEAR_IGNORED;utils.misc.formatDate=function(date,customFormat){var dateString=null;try{var format=customFormat||{month:'short',day:'numeric'};var offset=date.getTimezoneOffset()*60*1000;var normalizedDate=new Date(date.getTime()+offset);var year=normalizedDate.getFullYear();if(year!==FLAG_YEAR_IGNORED){format.year='numeric';}
|
||||
var f=new Intl.DateTimeFormat(navigator.languages,format);dateString=f.format(normalizedDate);}catch(err){console.error('Error parsing date: ',err);throw err;}
|
||||
return dateString;};utils.misc.parseName=function(nameString){var MIN_LENGHT_SIGNIFICATIVE=3;var MIN_LENGHT_FN=2;var out={givenName:'',familyName:''};if(!nameString){return out;}
|
||||
var str=nameString.trim();if(!str){return out;}
|
||||
function startsWithUpper(str){var firstLetter=str.charAt(0);var capFirstLetter=firstLetter.toLocaleUpperCase();return(capFirstLetter===firstLetter);}
|
||||
var tokens=nameString.split(/\s+/);var significativeTokens={};for(var j=0;j<tokens.length;j++){var token=tokens[j];if(token.length>MIN_LENGHT_SIGNIFICATIVE||startsWithUpper(token)){significativeTokens[token]=true;}}
|
||||
var totalTokens=tokens.length;var lastToken=totalTokens-1;var rolePrevToken;var remainingTokens=Object.keys(significativeTokens).length;var outGivenNames=[],outFamilyNames=[];var numFns=0;while(lastToken>=0){if(significativeTokens[tokens[lastToken]]){remainingTokens--;}
|
||||
var currToken=tokens[lastToken],nextToken=tokens[lastToken+1],prevToken=tokens[lastToken-1];if(!nextToken&&prevToken){outFamilyNames.push(currToken);numFns++;rolePrevToken='FN';}
|
||||
else if(!prevToken){outGivenNames.push(currToken);rolePrevToken='GN';}
|
||||
else if(nextToken){if(!significativeTokens[currToken]){if(rolePrevToken==='FN'){outFamilyNames.push(currToken);}
|
||||
else{outGivenNames.push(currToken);}}
|
||||
else if(currToken.length<MIN_LENGHT_FN){outGivenNames.push(currToken);rolePrevToken='GN';}
|
||||
else{if(remainingTokens>=2&&numFns<2){outFamilyNames.push(currToken);numFns++;rolePrevToken='FN';}
|
||||
else{outGivenNames.push(currToken);rolePrevToken='GN';}}}
|
||||
lastToken--;}
|
||||
out.givenName=outGivenNames.reverse().join(' ').trim();out.familyName=outFamilyNames.reverse().join(' ').trim();return out;};utils.misc.getPreferredPictureBox=function(){var imgThumbSize=oauthflow.params.facebook.imgThumbSize;var out={width:scale(imgThumbSize)};out.height=out.width;return out;};utils.misc.getPreferredPictureDetail=function(){var imgDetailWidth=oauthflow.params.facebook.imgDetailWidth;return scale(imgDetailWidth);};utils.misc.setTimestamp=function(type,callback){ImportStatusData.put(type+LAST_IMPORT_TIMESTAMP_SUFFIX,Date.now()).then(callback);};utils.misc.getTimestamp=function(type,callback){ImportStatusData.get(type+LAST_IMPORT_TIMESTAMP_SUFFIX).then(callback);};}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
;'use strict';var utils=window.utils||{};(function(){utils.overlay={};var overlay=document.querySelector('#loading-overlay'),statusContainer=overlay.querySelector('p[role="status"]'),progressActivity=document.querySelector('#progress-activity'),progressTitle=document.querySelector('#progress-title'),progressElement=document.querySelector('#progress-element'),progressMsg=document.querySelector('#progress-msg'),menu=document.querySelector('#loading-overlay menu'),cancelButton=document.querySelector('#cancel-overlay');function ProgressBar(pMsgId,pClass){var counter=0;var total=0;var progressTextId=pMsgId||'genericProgress';var clazz=pClass;progressElement.setAttribute('value',0);function showMessage(){var attributes=progressTextId=='infiniteProgress'?{n:counter}:{current:counter,total:total};document.l10n.setAttributes(progressMsg,progressTextId,attributes);}
|
||||
this.update=function(value){if(value&&value<=total&&value>=counter){counter=value;}else{counter++;}
|
||||
progressElement.setAttribute('value',((counter*100)/total).toFixed());showMessage();};this.setTotal=function(ptotal){total=ptotal;};this.setClass=function(clazzName){setClass(clazzName);clazz=clazzName;if(clazzName==='activityBar'||clazzName==='spinner'){progressMsg.removeAttribute('data-l10n-id');}};this.setHeaderMsg=function(headerMsgId){progressTitle.setAttribute('data-l10n-id',headerMsgId);};}
|
||||
utils.overlay.isAnimationPlaying=false;utils.overlay.isShown=false;utils.overlay._show=function _show(messageId,progressClass,textId){progressActivity.classList.remove('hide');if(typeof messageId==='string'){progressTitle.setAttribute('data-l10n-id',messageId);}else{document.l10n.setAttributes(progressTitle,messageId.id,messageId.args);}
|
||||
progressMsg.textContent=null;progressMsg.removeAttribute('data-l10n-id');if(utils.overlay.isShown){return;}
|
||||
overlay.classList.remove('hide');overlay.classList.remove('fade-out');overlay.classList.add('fade-in');utils.overlay.isAnimationPlaying=true;window.dispatchEvent(new CustomEvent('loadingoverlayshowing'));overlay.addEventListener('animationend',function ov_onFadeIn(ev){utils.overlay.isAnimationPlaying=false;overlay.removeEventListener('animationend',ov_onFadeIn);overlay.classList.remove('no-opacity');utils.overlay.isShown=true;});};utils.overlay.show=function show(messageId,progressClass,textId){var out;out=new ProgressBar(textId,progressClass);setClass(progressClass);utils.overlay.hideMenu();if(!utils.overlay.isAnimationPlaying){utils.overlay._show(messageId,progressClass,textId);return out;}
|
||||
overlay.addEventListener('animationend',function ov_showWhenFinished(ev){overlay.removeEventListener('animationend',ov_showWhenFinished);utils.overlay._show(messageId,progressClass,textId);});return out;};utils.overlay.showMenu=function showMenu(){menu.classList.add('showed');};utils.overlay.hideMenu=function hideMenu(){menu.classList.remove('showed');};Object.defineProperty(utils.overlay,'oncancel',{set:function(cancelCb){if(typeof cancelCb==='function'){cancelButton.disabled=false;cancelButton.onclick=function on_cancel(e){delete cancelButton.onclick;cancelCb();return false;};}}});function setAsProgress(){statusContainer.classList.remove('loading-icon');progressElement.setAttribute('max','100');progressElement.setAttribute('value','0');}
|
||||
function setClass(clazzName){switch(clazzName){case'spinner':progressElement.classList.remove('pack-activity');statusContainer.classList.add('loading-icon');progressElement.removeAttribute('max');progressElement.removeAttribute('value');break;case'activityBar':case'progressActivity':progressElement.classList.add('pack-activity');setAsProgress();break;case'progressBar':progressElement.classList.remove('pack-activity');setAsProgress();break;}}
|
||||
utils.overlay._hide=function ov__hide(){if(!utils.overlay.isShown){return;}
|
||||
overlay.classList.remove('fade-in');overlay.classList.add('fade-out');utils.overlay.isAnimationPlaying=true;window.dispatchEvent(new CustomEvent('loadingoverlayhiding'));overlay.addEventListener('animationend',function ov_onFadeOut(ev){utils.overlay.isAnimationPlaying=false;overlay.removeEventListener('animationend',ov_onFadeOut);progressActivity.classList.add('hide');overlay.classList.add('no-opacity');overlay.classList.add('hide');utils.overlay.isShown=false;});};utils.overlay.hide=function ov_hide(){if(!utils.overlay.isAnimationPlaying){utils.overlay._hide();return;}
|
||||
overlay.addEventListener('animationend',function ov_hideWhenFinished(ev){overlay.removeEventListener('animationend',ov_hideWhenFinished);utils.overlay._hide();});};})();
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
;'use strict';var utils=window.utils||{};if(!utils.sdcard){var SdCard=utils.sdcard={NOT_INITIALIZED:0,NOT_AVAILABLE:1,AVAILABLE:2,SHARED:3,observers:{}};SdCard.status=SdCard.NOT_INITIALIZED;SdCard.deviceStorages=navigator.getDeviceStorages('sdcard');SdCard.deviceStorage=Array.isArray(SdCard.deviceStorages)&&SdCard.deviceStorages[0];SdCard._toStatus=function toStatus(state){switch(state){case'available':SdCard.status=SdCard.AVAILABLE;break;case'shared':SdCard.status=SdCard.SHARED;break;case'unavailable':case'deleted':SdCard.status=SdCard.NOT_AVAILABLE;break;}};SdCard.updateStorageState=function sd_updateStorageState(state){SdCard._toStatus(state);Object.keys(this.observers).forEach(function onObserver(name){if(typeof(SdCard.observers[name])==='function'){SdCard.observers[name].call(null,state);}});};if(SdCard.deviceStorage){SdCard.deviceStorage.addEventListener('change',function sd_deviceStorageChangeHandler(e){SdCard.updateStorageState(e.reason);});if(SdCard.status===SdCard.NOT_INITIALIZED){SdCard.deviceStorage.available().onsuccess=(function(e){SdCard.updateStorageState(e.target.result);});}}
|
||||
SdCard.checkStorageCard=function sd_checkStorageCard(){return SdCard.status===SdCard.AVAILABLE;};SdCard.getStatus=function(cb){if(!SdCard.deviceStorage){SdCard._toStatus('unavailable');cb(SdCard.status);return;}
|
||||
var req=SdCard.deviceStorage.available();req.onsuccess=function(){SdCard._toStatus(req.result);cb(SdCard.status);};req.onerror=function(){console.error('Error while determining SD Status',req.error.name);cb(SdCard.status);};};SdCard.retrieveFiles=function retrieveFilesContent(mimes,exts,cb){var fileArray=[];var forbiddenDirectories=['.Trash'];var storages=navigator.getDeviceStorages('sdcard');var numberOfStorages=storages.length;var currentStorage=0;var cursorOnError=function(){cb(this.error.name);};var reExt=new RegExp('.('+exts.join('|')+')$');var cursorOnSuccess=function(e){var file=e.target.result;if(!file){if(++currentStorage<numberOfStorages){cursor=storages[currentStorage].enumerate();cursor.onsuccess=cursorOnSuccess;cursor.onerror=cursorOnError;}else{cb(null,fileArray);}}else{if((mimes.indexOf(file.type)===-1)&&!reExt.test(file.name)){cursor.continue();}else{if(forbiddenDirectories.indexOf(file.name)===-1){fileArray.push(file);cursor.continue();}}}};var cursor=storages[currentStorage].enumerate();cursor.onsuccess=cursorOnSuccess;cursor.onerror=cursorOnError;};SdCard.getTextFromFile=function(file,cb){if(!file){return Promise.reject();}
|
||||
return new Promise((resolve,reject)=>{const END_VCARD='end:vcard';const CHUNK_SIZE=1*1024*1024;var fileReader=new FileReader();var result=0;(function readChunk(offset,buffer){if(offset>=file.size){resolve(result);return;}
|
||||
var chunk=file.slice(offset,offset+CHUNK_SIZE);offset+=CHUNK_SIZE;fileReader.readAsText(chunk);fileReader.onload=function(){buffer+=fileReader.result;if(!buffer.match(/end:vcard/gi)){readChunk(offset,buffer);return;}
|
||||
var lastContactInChunk=buffer.toLowerCase().lastIndexOf(END_VCARD)+END_VCARD.length;cb(buffer.substr(0,lastContactInChunk)).then(partialResult=>{result+=partialResult;readChunk(offset,buffer.substr(lastContactInChunk+1));}).catch(error=>{console.warn(error);});};})(0,'');});};SdCard.subscribeToChanges=function(name,func,force){if(this.observers[name]!==undefined&&!force){return false;}
|
||||
this.observers[name]=func;return true;};SdCard.unsubscribeToChanges=function(name){if(this.observers[name]){delete this.observers[name];return true;}else{return false;}};}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
;'use strict';var utils=window.utils||{};utils.status=(function(){var STATUS_TIME=2000;var statusMsg=document.querySelector('#statusMsg');var hidingTimeout;var transitionEndTimeout;var additionalLine;var hideAnimationDone=function(){statusMsg.removeEventListener('transitionend',hideAnimationDone);statusMsg.classList.add('hidden');};var showAnimationDone=function(){if(transitionEndTimeout){clearTimeout(transitionEndTimeout);}
|
||||
statusMsg.removeEventListener('transitionend',showAnimationDone);hidingTimeout=setTimeout(utils.status.hide,STATUS_TIME);};var hideStatus=function(){statusMsg.addEventListener('transitionend',hideAnimationDone);statusMsg.classList.remove('opening');statusMsg.classList.remove('bannerStart');statusMsg.querySelector('p').removeAttribute('data-l10n-id');if(additionalLine){statusMsg.removeChild(additionalLine);additionalLine=null;}};var setL10nAttributes=function(node,l10n){if(l10n&&l10n.id){navigator.mozL10n.setAttributes(node,l10n.id,l10n.args);return true;}else{console.error('Status arguments must be objects');return false;}};var showStatus=function(mainMessage,extra){statusMsg.removeEventListener('transitionend',showAnimationDone);statusMsg.removeEventListener('transitionend',hideAnimationDone);LazyLoader.load([statusMsg],function _loaded(){if(!setL10nAttributes(statusMsg.querySelector('p'),mainMessage)){return;}
|
||||
if(extra){additionalLine=document.createElement('p');statusMsg.appendChild(additionalLine);setL10nAttributes(additionalLine,extra);}
|
||||
if(statusMsg.classList.contains('opening')){clearTimeout(hidingTimeout);hidingTimeout=setTimeout(hideStatus,STATUS_TIME);return;}
|
||||
statusMsg.classList.remove('hidden');statusMsg.addEventListener('transitionend',showAnimationDone);setTimeout(function displaying(){statusMsg.classList.add('opening');statusMsg.classList.add('bannerStart');transitionEndTimeout=setTimeout(showAnimationDone,STATUS_TIME);},10);});};return{show:showStatus,hide:hideStatus};})();
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
;'use strict';function parseDataUri(str){var re=/^data:(.+);(charset=([^;]+))?;?base64,(.*)$/;var matches=re.exec(str);if(matches){return{mime:matches[1],charset:matches[3],value:matches[4]};}
|
||||
return null;}
|
||||
function ensureMimeType(type){type=type.toLowerCase();if(!MimeMapper.isSupportedType(type)){type=MimeMapper.guessTypeFromExtension(type)||type;}
|
||||
return type||'';}
|
||||
function b64toBlob(b64Data,contentType,sliceSize){if(!b64Data){console.error('No b64 data provided to convert to blob.');return;}
|
||||
contentType=contentType||'';sliceSize=sliceSize||1024;function charCodeFromCharacter(c){return c.charCodeAt(0);}
|
||||
var blob;try{var byteCharacters=atob(b64Data);var byteArrays=[];for(var offset=0;offset<byteCharacters.length;offset+=sliceSize){var slice=byteCharacters.slice(offset,offset+sliceSize);var byteNumbers=[];for(var i=0,l=slice.length;i<l;i++){byteNumbers.push(charCodeFromCharacter(slice[i]));}
|
||||
var byteArray=new Uint8Array(byteNumbers);byteArrays.push(byteArray);}
|
||||
blob=new Blob(byteArrays,{type:contentType});}catch(e){console.error('Error parsing base64 data');}
|
||||
return blob;}
|
||||
var VCFReader=(function _VCFReader(){var ReBasic=/^([^:]+):(.+)$/;var ReTuple=/([a-zA-Z]+)=(.+)/;var WHITE_SPACE=' ';var DEFAULT_PHONE_TYPE='other';var VCARD_SIMPLE_TYPES={'fax':'faxOther','faxother':'faxOther','home':'home','internet':'internet','cell':'mobile','pager':'pager','personal':'home','pref':'pref','text':'text','textphone':'textphone','voice':'voice','work':'work'};var VCARD_COMPLEX_TYPES={'fax,work':'faxOffice','fax,home':'faxHome','voice,work':'work','voice,home':'home'};var REST_TIMEOUT=5000;function _parseTuple(p){var match=p.match(ReTuple);return match?[match[1].toLowerCase(),match[2]]:['type',p];}
|
||||
function _parseLine(line){var parsed=ReBasic.exec(line);if(!parsed){return null;}
|
||||
var tuples=parsed[1].split(/[;,]/);var fieldName=tuples.shift().toLowerCase();var fieldValue=/[^\s;]/.test(parsed[2])?parsed[2].split(';'):[];var meta={type:[]};var len=tuples.length;for(var i=0;i<len;i++){var tuple=_parseTuple(tuples[i]);if(tuple[0]==='type'){meta.type.push(tuple[1]);}else{meta[tuple[0]]=tuple[1];}}
|
||||
if(fieldName==='photo'){if(meta.type.length){meta.type=ensureMimeType(meta.type[0]);}
|
||||
fieldValue=parsed[2];if(meta.encoding&&(meta.encoding.toLowerCase()==='b'||meta.encoding.toLowerCase()==='base64')){meta.encoding='base64';}else{var parsedUri=parseDataUri(fieldValue);if(parsedUri){meta.type=ensureMimeType(parsedUri.mime);meta.encoding='base64';fieldValue=parsedUri.value;}}}
|
||||
return{key:fieldName,data:{meta:meta,value:fieldValue}};}
|
||||
var _parseEntries=function(cardArray,cb){var parsedCards=[];function sendIfFinished(contactObj){parsedCards.push(contactObj);if(parsedCards.length===cardArray.length){cb(parsedCards);}}
|
||||
for(var i=0;i<cardArray.length;i++){var lines=cardArray[i];if(!lines){sendIfFinished(null);continue;}
|
||||
var fields={};var len=lines.length;for(var j=0;j<len;j++){var line=lines[j];var parsedLine=_parseLine(line);if(!parsedLine){continue;}
|
||||
if(!fields[parsedLine.key]){fields[parsedLine.key]=[];}
|
||||
fields[parsedLine.key].push(parsedLine.data);}
|
||||
if(!fields.fn&&!fields.n){sendIfFinished(null);continue;}
|
||||
vcardToContact(fields,sendIfFinished);}};var qpRegexp=/=([a-zA-Z0-9]{2})/g;var _decodeQuoted=function(str){return decodeURIComponent(str.replace(qpRegexp,'%$1'));};function _decodeQP(metaObj,value){var isQP=metaObj&&metaObj.encoding&&(/quoted-printable/i).test(metaObj.encoding);if(isQP){value=_decodeQuoted(value);}
|
||||
return value;}
|
||||
var NAME_PARTS=['familyName','givenName','additionalName','honorificPrefix','honorificSuffix'];function _processName(vcardObj,contactObj){if(vcardObj.fn&&vcardObj.fn.length){var fnMeta=vcardObj.fn[0].meta;var fnValue=vcardObj.fn[0].value[0];contactObj.name=[_decodeQP(fnMeta,fnValue)];}
|
||||
if(vcardObj.n&&vcardObj.n.length){var values=vcardObj.n[0].value;var meta=vcardObj.n[0].meta;for(var i=0;i<values.length;i++){var namePart=values[i];if(namePart&&NAME_PARTS[i]){contactObj[NAME_PARTS[i]]=_decodeQP(meta,namePart).split(',');}}
|
||||
if(!contactObj.name){contactObj.name=[_decodeQP(meta,values.join(' ').trim())];}}
|
||||
contactObj.givenName=contactObj.givenName||contactObj.name;return contactObj;}
|
||||
var ADDR_PARTS=[null,null,'streetAddress','locality','region','postalCode','countryName'];function _processAddr(vcardObj,contactObj){if(!vcardObj.adr){return contactObj;}
|
||||
contactObj.adr=[];for(var i=0;i<vcardObj.adr.length;i++){var cur={};var adr=vcardObj.adr[i];if(adr.meta&&adr.meta.type){cur.type=adr.meta.type;}
|
||||
for(var j=2;j<adr.value.length;j++){var decoded=_decodeQP(adr.meta,adr.value[j]);if(decoded!==''){cur[ADDR_PARTS[j]]=decoded;}}
|
||||
contactObj.adr.push(cur);}
|
||||
return contactObj;}
|
||||
function _processComm(vcardObj,contactObj){contactObj.tel=[];['tel','email','url'].forEach(function field2field(field){if(!vcardObj[field]){return;}
|
||||
var len=vcardObj[field].length;var hasTypeMapper=function(x){return x.trim().toLowerCase();};var notTypeMapper=function(v,key){return v.meta[key].trim().toLowerCase();};var noType=function(field){return field!=='type';};var noPref=function(field){return field!=='pref';};var typeFilter=function(metaValue){return!!VCARD_SIMPLE_TYPES[metaValue];};for(var i=0;i<len;i++){var v=vcardObj[field][i];var metaValues;var cur={};if(v.meta){if(v.value){cur.value=_decodeQP(v.meta,v.value[0]);cur.value=cur.value.replace(/^tel:/i,'');}
|
||||
if(v.meta.type){metaValues=([].slice.call(v.meta.type)).map(hasTypeMapper);}else{metaValues=Object.keys(v.meta).filter(noType).map(notTypeMapper.bind(null,v));}
|
||||
if(metaValues.indexOf('pref')!==-1){cur.pref=true;metaValues=metaValues.filter(noPref);}
|
||||
switch(metaValues.length){case 0:cur.type=[DEFAULT_PHONE_TYPE];break;case 1:cur.type=[VCARD_SIMPLE_TYPES[metaValues[0]]||DEFAULT_PHONE_TYPE];break;case 2:var complexType1=metaValues[0]+','+metaValues[1];var complexType2=metaValues[1]+','+metaValues[0];cur.type=[VCARD_COMPLEX_TYPES[complexType1]||VCARD_COMPLEX_TYPES[complexType2]||VCARD_SIMPLE_TYPES[metaValues[0]]||VCARD_SIMPLE_TYPES[metaValues[1]]||DEFAULT_PHONE_TYPE];break;default:cur.type=[VCARD_SIMPLE_TYPES[metaValues.filter(typeFilter).shift()]||DEFAULT_PHONE_TYPE];}}
|
||||
if(!contactObj[field]){contactObj[field]=[];}
|
||||
contactObj[field].push(cur);}});return contactObj;}
|
||||
function _isValidDate(dateValue){return!isNaN(Date.parse(dateValue));}
|
||||
function _processFields(vcardObj,contactObj){['org','title','bday','anniversary'].forEach(function(field){if(!vcardObj[field]){return;}
|
||||
var v=vcardObj[field][0];if(!v||!Array.isArray(v.value)||v.value.length===0){return;}
|
||||
var dateValue;if(field==='bday'){dateValue=v.value[0];if(_isValidDate(dateValue)){contactObj.bday=new Date(dateValue);}
|
||||
return;}
|
||||
if(field==='anniversary'){dateValue=v.value[0];if(_isValidDate(dateValue)){contactObj.anniversary=new Date(dateValue);}
|
||||
return;}
|
||||
if(!v){return;}
|
||||
if(field==='title'){field='jobTitle';}
|
||||
switch(typeof v){case'object':contactObj[field]=[_decodeQP(v.meta,v.value[0])];break;case'string':contactObj[field]=[v];break;}});return contactObj;}
|
||||
function _processPhoto(vcardObj,contactObj,cb){if(!Array.isArray(vcardObj.photo)||vcardObj.photo.length===0||!vcardObj.photo[0]||!vcardObj.photo[0].value){return cb(contactObj);}
|
||||
var photo=vcardObj.photo[0];var photoContents=photo.value.trim();if(!photoContents){return cb(contactObj);}
|
||||
if(photoContents&&photo.meta&&photo.meta.encoding==='base64'){var blob=b64toBlob(photoContents,photo.meta.type);if(blob){utils.thumbnailImage(blob,function gotThumbnail(thumbnail){if(blob!==thumbnail){contactObj.photo=[blob,thumbnail];}else{contactObj.photo=[blob];}
|
||||
cb(contactObj);});}else{cb(contactObj);}}
|
||||
else{var callbacks={success:function(blobPic){if(blobPic){contactObj.photo=[blobPic];}
|
||||
cb(contactObj);},error:function(){console.error('Error getting photo for contact');cb(contactObj);},timeout:function(){console.error('Timeout getting photo for contact');cb(contactObj);}};Rest.get(photoContents,callbacks,{responseType:'blob',operationsTimeout:REST_TIMEOUT});}}
|
||||
var vcardToContact=function(vcard,cb){if(!vcard){return null;}
|
||||
var obj={};_processName(vcard,obj);_processAddr(vcard,obj);_processComm(vcard,obj);_processFields(vcard,obj);_processPhoto(vcard,obj,function(contactObj){cb(utils.misc.toMozContact(contactObj));});};var VCFReader=function(contents){this.contents=contents;this.processed=0;this.finished=false;this.currentChar=0;this.numDupsMerged=0;};VCFReader.CONCURRENCY=5;VCFReader.prototype.finish=function(){this.finished=true;};VCFReader.prototype.process=function(cb){var self=this;var match=this.contents.match(/end:vcard/gi);if(!match){if(cb){cb();}
|
||||
return;}
|
||||
this.importedContacts=[];this.total=match.length;this.onread&&this.onread(this.total);this.ondone=function(numImported){cb(numImported,self.numDupsMerged);};LazyLoader.load(['/shared/js/simple_phone_matcher.js','/shared/js/mime_mapper.js','/shared/js/contact_photo_helper.js','/shared/js/contacts/import/utilities/misc.js','/shared/js/contacts/contacts_matcher.js','/shared/js/contacts/contacts_merger.js','/shared/js/contacts/utilities/image_thumbnail.js','/shared/js/contacts/merger_adapter.js','/shared/js/contacts/utilities/http_rest.js'],function(){this.splitLines();}.bind(this));};VCFReader.prototype.onParsed=function(err,ct){this.processed+=1;this.importedContacts.push(ct);this.onimported&&this.onimported(ct&&ct.name);if(this.finished||this.processed===this.total){this.ondone(this.importedContacts);return;}
|
||||
var processed=this.processed;if(processed<this.total&&processed%VCFReader.CONCURRENCY===0){this.splitLines();}};VCFReader.prototype.post=function(contactObjects){var _onParsed=this.onParsed.bind(this);var cursor=0;var self=this;function afterSave(ct,e){_onParsed(e,ct);cursor+=1;if(cursor<contactObjects.length){saveContact(contactObjects[cursor]);}}
|
||||
function saveContact(ct){if(!ct){afterSave(null,null);return;}
|
||||
var contact=utils.misc.toMozContact(ct);var afterSaveFn=afterSave.bind(null,contact);var matchCbs={onmatch:function(matches){var callbacks={success:function(mergedContact){self.numDupsMerged++;afterSave(mergedContact,null);},error:afterSaveFn};contacts.adaptAndMerge(contact,matches,callbacks);},onmismatch:function(){VCFReader._save(contact,afterSaveFn);}};Matcher.match(contact,'passive',matchCbs);}
|
||||
saveContact(contactObjects[cursor]);};VCFReader._save=function(item,cb){var req=navigator.mozContacts.save(utils.misc.toMozContact(item));req.onsuccess=cb;req.onerror=cb;};var reBeginCard=/begin:vcard$/i;var reEndCard=/end:vcard$/i;var reVersion=/^VERSION:([\d\.]*)/i;VCFReader.prototype.splitLines=function(){var currentLine='';var currentVersion=0;var inLabel=false;var multiline=false;var cardArray=[[]];var cardsProcessed=0;var i=this.currentChar;var callPost=this.post.bind(this);for(var l=this.contents.length;i<l;i++){this.currentChar=i;var ch=this.contents[i];if(currentVersion>=4&&ch==='"'){inLabel=!inLabel;currentLine+=ch;continue;}
|
||||
if(multiline===true){if((/[\r\t\s\n]/).test(ch)){continue;}else{multiline=false;}}
|
||||
var next=this.contents[i+1];if(inLabel||(ch!=='\n'&&ch!=='\r')){if(!(/^PHOTO/i).test(currentLine)&&ch==='='&&next&&next.search(/(\r|\n)/)!==-1){multiline=true;continue;}
|
||||
currentLine+=ch;if(i!==l-1){continue;}}
|
||||
var firstCharNextLine=this.contents[i+2];if((firstCharNextLine===WHITE_SPACE)&&(/[\r\t\s\n]/).test(next)&&(/^PHOTO/i).test(currentLine)){multiline=true;continue;}
|
||||
if(next&&(next===WHITE_SPACE||next==='\t')){multiline=true;continue;}
|
||||
if(reBeginCard.test(currentLine)){currentLine='';currentVersion=0;continue;}
|
||||
if(reEndCard.test(currentLine)){cardsProcessed+=1;if(cardsProcessed===VCFReader.CONCURRENCY||(cardsProcessed+this.processed)===this.total){_parseEntries(cardArray,callPost);break;}
|
||||
currentLine='';cardArray.push([]);continue;}
|
||||
if(currentLine){var matches=reVersion.exec(currentLine);if(matches===null){cardArray[cardArray.length-1].push(currentLine);}else{currentVersion=parseFloat(matches[1]||'0');}}
|
||||
currentLine='';}};VCFReader._decodeQuoted=_decodeQuoted;VCFReader.processAddr=_processAddr;VCFReader.processName=_processName;VCFReader.vcardToContact=vcardToContact;VCFReader.utils={parseDataUri:parseDataUri,b64toBlob:b64toBlob};return VCFReader;})();
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
;'use strict';var contacts=window.contacts||{};contacts.adaptAndMerge=function(incomingContact,matches,callbacks){var listIds=Object.keys(matches);var totalMatches=listIds.length;var masterContact=matches[listIds[0]].matchingContact;var matchingContacts=[];for(var j=1;j<totalMatches;j++){matchingContacts.push(matches[listIds[j]]);}
|
||||
matchingContacts.push({matchingContact:incomingContact});contacts.Merger.merge(masterContact,matchingContacts,callbacks);};
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
;'use strict';(function(exports){var inSearchMode=false,searchView,searchBox,searchList,searchNoResult,searchProgress,searchTimer=null,contactNodes=null,selectableForm=null,searchableNodes=null,currentTextToSearch='',currentSearchTerms=[],prevTextToSearch='',currentSet={},searchTextCache={},canReuseSearchables=false,blurList=false,theClones={},CHUNK_SIZE=10,searchPageSize=-1,hardLimit=25,emptySearch=true,remainingPending=true,imgLoader,searchEnabled=false,source=null,navigationController=null;var init=function load(_source,defaultEnabled,navigation){searchView=document.getElementById('search-view');searchList=document.getElementById('search-list');selectableForm=document.getElementById('selectable-form');if(!_source){throw new Error('Search requires a contact source!');}
|
||||
source=_source;if(typeof source.click==='function'){searchList.addEventListener('click',source.click);}
|
||||
searchEnabled=!!defaultEnabled;navigationController=navigation||window.MainNavigation;};var initialized=false;var ignoreReturnKey=function ignoreReturnKey(evt){if(evt.keyCode==13){evt.preventDefault();}};var doInit=function doInit(){if(initialized){return;}
|
||||
utils.listeners.add({'#cancel-search':exitSearchMode,'#search-contact':[{event:'keypress',handler:ignoreReturnKey}]});initialized=true;searchBox=document.getElementById('search-contact');var resetButton=searchBox.nextElementSibling;resetButton.addEventListener('ontouchstart'in window?'touchstart':'mousedown',function(){searchBox.value='';searchBox.focus();resetState();window.setTimeout(fillInitialSearchPage,0);});searchList.parentNode.addEventListener('touchstart',function(){if(searchableNodes&&remainingPending){addRemainingResults(searchableNodes,searchPageSize);}
|
||||
blurList=true;});searchNoResult=document.getElementById('no-result');searchProgress=document.getElementById('search-progress');searchBox.addEventListener('blur',function(){window.setTimeout(onSearchBlur,0);});searchBox.addEventListener('focus',function(){blurList=false;});LazyLoader.load(['/contacts/js/fb_resolver.js','/shared/js/contacts/utilities/image_loader.js'],function(){imgLoader=new ImageLoader('#groups-list-search','li');imgLoader.setResolver(fb.resolver);});};var clearHighlights=function(node){var highlights=node.getElementsByTagName('mark');while(highlights.length){var parent=highlights[0].parentNode;while(highlights[0].firstChild){parent.insertBefore(highlights[0].firstChild,highlights[0]);}
|
||||
parent.removeChild(highlights[0]);}};var highlightNode=function(node){function doHighlight(text){if(text===''||text===' '){return document.createTextNode('');}
|
||||
return HtmlHelper.createHighlightHTML(text,currentSearchTerms);}
|
||||
function locateHTMLTag(text){var tagLocation=[];tagLocation.push(text.indexOf('<'));tagLocation.push(text.indexOf('>',tagLocation[0])+1);tagLocation.push(text.indexOf('<',tagLocation[1]));tagLocation.push(text.indexOf('>',tagLocation[2])+1);return tagLocation;}
|
||||
var textNode=node.querySelector('.contact-text bdi');if(textNode===null){return;}
|
||||
var text=textNode.innerHTML;var tagPos=locateHTMLTag(text);var beforeTag=text.substring(0,tagPos[0]);var inTag=text.substring(tagPos[1],tagPos[2]);var afterTag=text.substring(tagPos[3]);var realText=[beforeTag,inTag,afterTag];if(tagPos[0]==-1){var content=doHighlight(Normalizer.unescapeHTML(text));textNode.textContent='';textNode.appendChild(content);}else{for(var i=0,len=realText.length;i<len;i++){realText[i]=doHighlight(Normalizer.unescapeHTML(realText[i]));}
|
||||
var fragment=document.createDocumentFragment();var strongTag=document.createElement('strong');strongTag.appendChild(realText[1]);fragment.appendChild(realText[0]);fragment.appendChild(strongTag);fragment.appendChild(realText[2]);textNode.textContent='';textNode.appendChild(fragment);}};var updateSearchList=function updateSearchList(cb){if(!inSearchMode){if(cb){cb();}
|
||||
return;}
|
||||
window.setTimeout(function(){resetStateAndCache();search(cb);});};var resetStateAndCache=function resetStateAndCache(){contactNodes=null;searchTextCache={};resetState();};var exitSearchMode=function exitSearchMode(evt){if(evt){evt.preventDefault();}
|
||||
searchView.classList.remove('insearchmode');if(selectableForm){selectableForm.classList.remove('insearchmode');}
|
||||
if(navigationController){navigationController.back();}
|
||||
window.setTimeout(function exit_search(){hideProgressResults();searchBox.value='';contactNodes=null;searchTextCache={};resetState();inSearchMode=false;},0);window.removeEventListener('input',onInput);};function resetState(){prevTextToSearch='';currentTextToSearch='';currentSearchTerms=[];searchableNodes=null;canReuseSearchables=false;currentSet={};theClones={};utils.dom.removeChildNodes(searchList);emptySearch=true;remainingPending=true;}
|
||||
function addRemainingResults(nodes,from){if(remainingPending!==true){return;}
|
||||
var fragment=document.createDocumentFragment();for(var i=from;i<hardLimit&&i<nodes.length;i++){var node=nodes[i].node;var clon=getClone(node);highlightNode(clon);fragment.appendChild(clon);currentSet[node.dataset.uuid]=clon;}
|
||||
if(fragment.hasChildNodes()){searchList.appendChild(fragment);imgLoader.reload();}
|
||||
remainingPending=false;}
|
||||
function onSearchBlur(e){if(canReuseSearchables&&searchableNodes&&searchView.classList.contains('insearchmode')&&blurList){addRemainingResults(searchableNodes,searchPageSize);}
|
||||
else if(emptySearch===true&&remainingPending===true){var lastNode=searchList.querySelector('li:last-child');if(lastNode){var lastNodeUid=lastNode.dataset.uuid;var startNode=source.getNextNode(source.getNodeById(lastNodeUid));fillIdentityResults(startNode,hardLimit-searchPageSize);remainingPending=false;imgLoader.reload();}}}
|
||||
function fillIdentityResults(startNode,number){var fragment=document.createDocumentFragment();var contact=startNode;for(var i=0;i<number&&contact;i++){var clonedNode=getClone(contact);fragment.appendChild(clonedNode);currentSet[contact.dataset.uuid]=clonedNode;contact=source.getNextNode(contact);}
|
||||
if(fragment.hasChildNodes()){searchList.appendChild(fragment);}}
|
||||
function getClone(node){var id=node.dataset.uuid;var out=theClones[id];if(!out){out=source.clone(node);cacheClone(id,out);}
|
||||
return out;}
|
||||
function cacheClone(id,clone){theClones[id]=clone;}
|
||||
function onInput(e){if(e.target.id===searchBox.id){search();}}
|
||||
var enterSearchMode=function searchMode(evt){evt.preventDefault();if(!inSearchMode){window.addEventListener('input',onInput);searchView.classList.add('insearchmode');if(selectableForm){selectableForm.classList.add('insearchmode');}
|
||||
doInit();fillInitialSearchPage();inSearchMode=true;emptySearch=true;if(navigationController){navigationController.go('search-view','none');}
|
||||
setTimeout(function nextTick(){searchBox.focus();});}};function fillInitialSearchPage(done){hideProgressResults();var startContact=source.getFirstNode();var numToFill=searchPageSize;if(startContact&&searchPageSize<1){fillIdentityResults(startContact,1);var viewHeight=searchList.getBoundingClientRect().height;var rowHeight=searchList.children[0].getBoundingClientRect().height;searchPageSize=Math.ceil(viewHeight/rowHeight);hardLimit=~~(3.5*searchPageSize);startContact=source.getNextNode(startContact);numToFill=searchPageSize-1;}
|
||||
fillIdentityResults(startContact,numToFill);if(typeof done==='function'){done();}
|
||||
if(imgLoader){imgLoader.reload();}}
|
||||
function doSearch(contacts,from,searchText,pattern,state){if(currentTextToSearch.localeCompare(searchText)!==0){canReuseSearchables=false;window.console.warn('**** Cancelling current search ****');return;}
|
||||
var end=from+CHUNK_SIZE;currentSearchTerms=pattern.source.split(/\s+/);for(var c=from;c<end&&c<contacts.length;c++){var contact=contacts[c].node||contacts[c];var contactText=contacts[c].text||getSearchText(contacts[c]);contactText=Normalizer.unescapeHTML(contactText);if(!checkContactMatch(currentSearchTerms,contactText)){if(contact.dataset.uuid in currentSet){searchList.removeChild(currentSet[contact.dataset.uuid]);delete currentSet[contact.dataset.uuid];}}else{if(state.count===0){hideProgressResults();}
|
||||
if(Object.keys(currentSet).length<searchPageSize&&!(contact.dataset.uuid in currentSet)){var clonedNode=getClone(contact);currentSet[contact.dataset.uuid]=clonedNode;searchList.appendChild(clonedNode);}
|
||||
if(currentSet[contact.dataset.uuid]){clearHighlights(currentSet[contact.dataset.uuid]);highlightNode(currentSet[contact.dataset.uuid]);}
|
||||
state.searchables.push({node:contact,text:contactText});state.count++;}}
|
||||
if(c<contacts.length){searchTimer=window.setTimeout(function do_search(){searchTimer=null;doSearch(contacts,from+CHUNK_SIZE,searchText,pattern,state);},0);return;}else if(source.expectMoreNodes()){var delay=250;searchTimer=window.setTimeout(function do_search(){doSearch(contacts,Math.min(end,contacts.length),searchText,pattern,state);},delay);return;}else if(state.count===0){showNoResults();canReuseSearchables=false;}else{imgLoader.reload();searchableNodes=state.searchables;canReuseSearchables=true;if(blurList===true){searchTimer=window.setTimeout(function(){searchTimer=null;addRemainingResults(searchableNodes,searchPageSize);},0);}}
|
||||
if(typeof state.searchDoneCb==='function'){state.searchDoneCb();}}
|
||||
var enableSearch=function enableSearch(){if(searchEnabled){return;}
|
||||
searchEnabled=true;invalidateCache();search();};var appendNodes=function appendNodes(nodes){if(!nodes||!nodes.length||!contactNodes){return;}
|
||||
contactNodes.push.apply(contactNodes,nodes);if(!currentTextToSearch||!canReuseSearchables||!searchableNodes){return;}
|
||||
for(var i=0,n=nodes.length;i<n;++i){var node=nodes[i];var nodeText=Normalizer.unescapeHTML(getSearchText(node));if(!checkContactMatch(currentSearchTerms,nodeText)){searchableNodes.push({node:node,text:nodeText});}}};var checkContactMatch=function checkContactMatch(searchTerms,text){for(var i=0,m=searchTerms.length;i<m;i++){if(!RegExp(searchTerms[i],'i').test(text)){return false;}}
|
||||
return true;};var search=function performSearch(searchDoneCb){prevTextToSearch=currentTextToSearch;currentTextToSearch=Normalizer.toAscii(searchBox.value.trim());currentTextToSearch=Normalizer.escapeRegExp(currentTextToSearch);var thisSearchText=String(currentTextToSearch);if(thisSearchText.length===0){resetState();window.setTimeout(fillInitialSearchPage,0,searchDoneCb);}
|
||||
else{showProgress();if(!searchEnabled){resetState();return;}
|
||||
emptySearch=false;remainingPending=true;var pattern=new RegExp(thisSearchText,'i');var contactsToSearch=getContactsToSearch(thisSearchText,prevTextToSearch);var state={count:0,searchables:[],searchDoneCb:searchDoneCb};searchTimer=window.setTimeout(function do_search(){searchTimer=null;doSearch(contactsToSearch,0,thisSearchText,pattern,state);},0);}};function getSearchText(contact){var out='';var uuid=contact.dataset.uuid;if(uuid){out=searchTextCache[uuid];if(!out){out=source.getSearchText(contact);searchTextCache[uuid]=out;}}
|
||||
else{window.console.error('Search: Not uuid found for the provided node');}
|
||||
return out;}
|
||||
var getContactsDom=function contactsDom(){if(!contactNodes){contactNodes=source.getNodes();}
|
||||
return contactNodes;};var getContactsToSearch=function getContactsToSearch(newText,prevText){var out;if(canReuseSearchables&&newText.length>prevText.length&&prevText.length>0&&newText.indexOf(prevText)===0){out=searchableNodes||getContactsDom();}else{utils.dom.removeChildNodes(searchList);currentSet={};out=getContactsDom();canReuseSearchables=false;}
|
||||
return out;};var isInSearchMode=function isInSearchMode(){return inSearchMode;};var invalidateCache=function s_invalidate(){if(searchTimer){window.clearTimeout(searchTimer);searchTimer=null;}
|
||||
currentTextToSearch='';canReuseSearchables=false;searchableNodes=null;contactNodes=null;currentSet={};searchTextCache={};};var removeContact=function s_removeContact(id){var contact=searchList.querySelector('li[data-uuid=\"'+id+'\"]');searchList.removeChild(contact);};var selectRow=function s_selectRow(id,value){var check=searchList.querySelector('#search-view input[value="'+id+'"]');if(check){check.checked=value;}};function showProgress(){searchNoResult.classList.add('hide');searchProgress.classList.remove('hidden');}
|
||||
function showNoResults(){searchNoResult.classList.remove('hide');searchProgress.classList.add('hidden');}
|
||||
function hideProgressResults(){searchNoResult.classList.add('hide');searchProgress.classList.add('hidden');}
|
||||
exports.Search={'init':init,'invalidateCache':invalidateCache,'appendNodes':appendNodes,'removeContact':removeContact,'search':search,'enterSearchMode':enterSearchMode,'exitSearchMode':exitSearchMode,'isInSearchMode':isInSearchMode,'enableSearch':enableSearch,'selectRow':selectRow,'updateSearchList':updateSearchList};})(window);
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
;'use strict';var utils=window.utils||{};if(!utils.dom){(function(document){var Dom=utils.dom={};Dom.removeChildNodes=function(node){while(node.hasChildNodes()){node.removeChild(node.firstChild);}};Dom.addClassToNodes=function(container,selector,clazz){var nodes=container.querySelectorAll(selector);for(var i=0,n=nodes.length;i<n;++i){nodes[i].classList.add(clazz);}};Dom.removeClassFromNodes=function(container,selector,clazz){var nodes=container.querySelectorAll(selector);for(var i=0,n=nodes.length;i<n;++i){nodes[i].classList.remove(clazz);}};Dom.updatePhoto=function(photo,dest){var background='';if(photo!=null){background='url('+URL.createObjectURL(photo)+')';}
|
||||
dest.style.backgroundImage=background;dest.dataset.photoReady='true';};})(document);}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
;'use strict';var utils=window.utils||{};if(!utils.listeners){(function(document){var Listeners=utils.listeners={};Listeners.add=function(config){try{for(var id in config){var handler=config[id];var nodes=document.querySelectorAll(id);for(var i=0;i<nodes.length;i++){var node=nodes[i];if(Array.isArray(handler)){handler.forEach(function handle(item){if(!item.hasOwnProperty('event')&&!item.hasOwnProperty('handler')){return;}
|
||||
node.addEventListener(item.event,item.handler);});}else{node.addEventListener('click',handler);}}}}
|
||||
catch(e){window.console.error('Error while registering listener for: ',id,e);}};})(document);}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
;'use strict';if(!window.Rest){window.Rest=(function(){function RestRequest(xhr){var cancelled=false;this.cancel=function oncancel(){cancelled=true;window.setTimeout(xhr.abort.bind(xhr),0);};this.isCancelled=function isCancelled(){return cancelled;};}
|
||||
function Rest(){}
|
||||
Rest.prototype={get:function(uri,callback,pOptions){var DEFAULT_TIMEOUT=30000;var options=pOptions||{};var xhr=new XMLHttpRequest({mozSystem:true});var outReq=new RestRequest(xhr);xhr.open('GET',uri,true);var responseType=options.responseType||'json';xhr.responseType=responseType;var responseProperty=responseType==='xml'?'responseXML':'response';xhr.timeout=options.operationsTimeout||DEFAULT_TIMEOUT;if(!xhr.timeout||xhr.timeout===DEFAULT_TIMEOUT&&(parent&&parent.config&&parent.config.operationsTimeout)){xhr.timeout=parent.config.operationsTimeout;}
|
||||
if(options.requestHeaders){for(var header in options.requestHeaders){xhr.setRequestHeader(header,options.requestHeaders[header]);}}
|
||||
xhr.onload=function(e){if(xhr.status===200||xhr.status===400||xhr.status===0){if(callback&&typeof callback.success==='function'){setTimeout(function(){callback.success(xhr[responseProperty]);},0);}}
|
||||
else{console.error('HTTP error executing GET. ',uri,' Status: ',xhr.status);if(callback&&typeof callback.error==='function'){setTimeout(function errorHandler(){callback.error({status:xhr.status});},0);}}};xhr.ontimeout=function(e){console.error('Timeout!!! while HTTP GET: ',uri);if(callback&&typeof callback.timeout==='function'){setTimeout(callback.timeout,0);}};xhr.onerror=function(e){console.error('Error while executing HTTP GET: ',uri,': ',e);if(callback&&typeof callback.error==='function'&&!outReq.isCancelled()){setTimeout(function(){callback.error(e);},0);}};xhr.send();return outReq;}};return new Rest();})();}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
;'use strict';if(!window.ImageLoader){var ImageLoader=function ImageLoader(pContainer,pItems){var container,items,itemsSelector,lastScrollTime,scrollLatency=100,scrollTimer,itemHeight,total,imgsLoading=0,loadImage=defaultLoadImage,self=this;init(pContainer,pItems);function init(pContainer,pItems){itemsSelector=pItems;container=document.querySelector(pContainer);attachHandlers();window.addEventListener('image-loader-resume',resuming);window.addEventListener('image-loader-pause',unload);load();}
|
||||
function resuming(){window.clearTimeout(scrollTimer);attachHandlers();update();}
|
||||
function onUpdate(evt){evt.stopPropagation();onScroll();}
|
||||
function load(){window.clearTimeout(scrollTimer);scrollTimer=null;items=container.querySelectorAll(itemsSelector);itemHeight=items[0]?items[0].offsetHeight:1;total=items.length;window.setTimeout(update,0);}
|
||||
function attachHandlers(){container.addEventListener('scroll',onScroll);document.addEventListener('onupdate',onUpdate);}
|
||||
function unload(){container.removeEventListener('scroll',onScroll);document.removeEventListener('onupdate',onUpdate);window.clearTimeout(scrollTimer);scrollTimer=null;}
|
||||
function setResolver(pResolver){loadImage=pResolver;}
|
||||
function onScroll(){if(imgsLoading>0){window.stop();imgsLoading=0;}
|
||||
lastScrollTime=Date.now();if(!scrollTimer){scrollTimer=window.setTimeout(updateFromScroll,scrollLatency);}}
|
||||
function updateFromScroll(){scrollTimer=null;var deltaLatency=lastScrollTime-Date.now()+scrollLatency;if(deltaLatency>0){scrollTimer=window.setTimeout(updateFromScroll,deltaLatency);}else{update();}}
|
||||
function defaultLoadImage(item){var image=item.querySelector('span[data-type=img][data-src]');if(!image){image=item.querySelector('img[data-src]');if(!image){image=item.querySelector('span[data-type=img][data-group]');if(image){item.dataset.visited='true';}
|
||||
return;}}
|
||||
++imgsLoading;var tmp=new Image();var src=tmp.src=image.dataset.src;tmp.onload=function onload(){--imgsLoading;image.style.backgroundImage='url('+src+')';if(tmp.complete){item.dataset.visited='true';}
|
||||
tmp=null;};tmp.onabort=tmp.onerror=function onerror(){--imgsLoading;item.dataset.visited='false';tmp=null;};}
|
||||
function update(){if(total===0){return;}
|
||||
var viewTop=container.scrollTop;var index=Math.floor(viewTop/itemHeight);var containerHeight=container.offsetHeight;for(var i=index;i>=0;i--){var item=items[i];if(item){if(item.offsetTop+itemHeight<viewTop){break;}
|
||||
if(item.dataset.visited!=='true'&&item.offsetTop<=viewTop+containerHeight){loadImage(item,self);}}}
|
||||
for(var j=index+1;j<total;j++){var theItem=items[j];if(!theItem){return;}
|
||||
if(theItem.offsetTop>viewTop+containerHeight){return;}
|
||||
if(theItem.dataset.visited!=='true'){loadImage(theItem,self);}}}
|
||||
function releaseImage(item){var image=item.querySelector('span[data-type=img][data-src]');if(!image){image=item.querySelector('span[data-type=img][data-group]');if(image){item.dataset.visited='false';}
|
||||
return null;}
|
||||
image.style.backgroundImage='none';item.dataset.visited='false';return image;}
|
||||
function destroy(){unload();window.removeEventListener('image-loader-pause',unload);window.removeEventListener('image-loader-resume',resuming);container=items=itemsSelector=lastScrollTime=scrollLatency=null;scrollTimer=itemHeight=total=imgsLoading=loadImage=null;}
|
||||
this.reload=load;this.unload=unload;this.setResolver=setResolver;this.defaultLoad=defaultLoadImage;this.releaseImage=releaseImage;this.destroy=destroy;};}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
;'use strict';var utils=window.utils||{};if(typeof utils.squareImage==='undefined'){utils.squareImage=function(blob,callback){var img=document.createElement('img');var url=URL.createObjectURL(blob);img.src=url;function cleanupImg(){img.src='';URL.revokeObjectURL(url);}
|
||||
img.onerror=cleanupImg;img.onload=function onBlobLoad(){var width=img.width;var height=img.height;if(width===height){cleanupImg();callback(blob);}else{var canvas=document.createElement('canvas');var min=canvas.width=canvas.height=Math.min(width,height);var context=canvas.getContext('2d',{willReadFrequently:true});context.drawImage(img,(width-min)/2,(height-min)/2,min,min,0,0,min,min);cleanupImg();canvas.toBlob(function onCanvasToBlob(canvasBlob){context=null;canvas.width=canvas.height=0;canvas=null;callback(canvasBlob);},'image/jpeg',0.95);}};};}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
;'use strict';var utils=window.utils||{};(function(utils){var DEPENDENCIES=['/shared/js/contacts/import/utilities/config.js','/shared/js/image_utils.js'];var dpr=window.devicePixelRatio||1;var CONFIG_FILE='/config-images.json';var DEFAULT_CONFIG={'thumbnail':{'format':'image/jpeg','size':65,'quality':1.0}};var THUMB_CONFIG=DEFAULT_CONFIG.thumbnail;if(typeof utils.thumbnailImage!=='undefined'){return;}
|
||||
function getThumbsConfig(){return new Promise(function(resolve,reject){utils.config.load(CONFIG_FILE).then(function resolved(config){resolve(config);},function rejected(){resolve(DEFAULT_CONFIG);});});}
|
||||
function scaleImage(blob,configData){var thumbConfig=configData&&configData.thumbnail||{};var thumbnailEdge=(thumbConfig.size||THUMB_CONFIG.size)*dpr;var format=thumbConfig.format||THUMB_CONFIG.format;var encodingQuality=thumbConfig.quality||THUMB_CONFIG.quality;return ImageUtils.resizeAndCropToCover(blob,thumbnailEdge,thumbnailEdge,format,encodingQuality);}
|
||||
utils.thumbnailImage=function(blob,callback){LazyLoader.load(DEPENDENCIES,function(){getThumbsConfig().then(function(config){return scaleImage(blob,config);}).then(callback).catch(function(err){console.error('Error while converting image to thumbnail:',err.name);callback(blob);});});};})(utils);
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
;'use strict';var utils=window.utils||{};if(!utils.templates){(function(){var Templates=utils.templates={};function getTarget(element){var target=element;if(!element.tagName){target=document.querySelector(element);}
|
||||
return target;}
|
||||
function getTemplate(target,data){var templates=target.querySelectorAll('*[data-template]');if(templates.length===0){throw new Error('No template declared');}
|
||||
if(templates.length>1&&templates.item(0).dataset.condition){throw new Error('Only one template supported in this version');}
|
||||
return{template:templates.item(0),isMulti:false};}
|
||||
function templateReplace(data){return function(text,property){var out;if(property.indexOf('.')===-1){out=data[property];if(Array.isArray(out)&&out.length>0){out=out[0];}}else{throw new Error('Dotted expressions not supported');}
|
||||
if(typeof out==='undefined'){out=text;}
|
||||
return out;};}
|
||||
function add(element,data,mode,targetNode){var target=getTarget(element);var newElem;var theData=[data];if(data instanceof Array){theData=data;}
|
||||
var multiTemplate=true;var template;var idx=0;theData.forEach(function(oneData){oneData._idx_=idx++;if(multiTemplate===true){var tresult=getTemplate(target,oneData);template=tresult.template;if(tresult.isMulti===false){multiTemplate=false;}}
|
||||
if(template){newElem=this.render(template,oneData);target=targetNode||target;if(mode==='A'){target.appendChild(newElem);}else if(mode==='P'){if(target.firstChild){target.insertBefore(newElem,element.firstChild);}else{target.appendChild(newElem);}}}}.bind(Templates));return newElem;}
|
||||
Templates.append=function(element,data,targetNode){var f=add.bind(this);return f(element,data,'A',targetNode);};Templates.prepend=function(element,data,targetNode){var f=add.bind(this);return f(element,data,'P',targetNode);};Templates.render=function(eleTemplate,data){var newElem=eleTemplate.cloneNode(true);newElem.removeAttribute('data-template');newElem.removeAttribute('data-condition');var pattern=/#(\w+[\w.]*)#/g;var inner=newElem.innerHTML;var replaceFunction=templateReplace(data);var ninner=inner.replace(pattern,replaceFunction);newElem.innerHTML=ninner;var attrs=newElem.attributes;var total=attrs.length;for(var c=0;c<total;c++){var val=attrs[c].value;var nval=val.replace(pattern,replaceFunction);newElem.setAttribute(attrs[c].name,nval);}
|
||||
if(!newElem.id){if(data.id){newElem.id=data.id;}}
|
||||
return newElem;};Templates.clear=function(element){var target=getTarget(element);var templates=target.querySelectorAll('*[data-template]');target.innerHTML='';var total=templates.length;for(var c=0;c<total;c++){target.appendChild(templates.item(c));}};})();}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
;(function(){'use strict';if(window.navigator.mozHour12||window.navigator.hour12){return;}
|
||||
window.navigator.mozHour12=undefined;var _setMozHour12=function(result){if(result===null){result=undefined;}
|
||||
if(window.navigator.mozHour12!==result){window.navigator.mozHour12=result;window.dispatchEvent(new CustomEvent('timeformatchange'));}};var _hour12Handler=function(event){_setMozHour12(event.settingValue);};var _kLocaleTime='locale.hour12';var req=window.navigator.mozSettings.createLock().get(_kLocaleTime);req.onsuccess=function(){_setMozHour12(req.result[_kLocaleTime]);};window.navigator.mozSettings.addObserver(_kLocaleTime,_hour12Handler);})();
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
;'use strict';function eventSafety(obj,event,callback,timeout){if(typeof callback==='number'){timeout=callback;callback=null;}
|
||||
if(!timeout){throw new Error('You must pass a valid timeout value to `eventSafety`.');}
|
||||
return new Promise((resolve)=>{var finishTimeout;function done(e){if(e&&e.target!==obj&&(e.type==='transitionend'||e.type==='animationend')){return;}
|
||||
clearTimeout(finishTimeout);obj.removeEventListener(event,done);if(callback){resolve(callback.apply(this,arguments));}else{resolve(e);}}
|
||||
finishTimeout=setTimeout(done,timeout);obj.addEventListener(event,done);});}
|
||||
423
assets/webapps/ftu.gaiamobile.org/shared/js/fb/fb_data_reader.js
Normal file
|
|
@ -0,0 +1,423 @@
|
|||
'use strict';
|
||||
/* global LazyLoader, TelIndexer */
|
||||
|
||||
// Reader Module for FB Data in a Datastore
|
||||
// To use this library you need to include 'shared/js/fb/fb_request.js'
|
||||
// WARNING: This file lazy loads 'shared/js/fb/fb_tel_index.js'
|
||||
|
||||
var fb = self.fb || {};
|
||||
self.fb = fb;
|
||||
|
||||
(function() {
|
||||
var contacts = fb.contacts || {};
|
||||
fb.contacts = contacts;
|
||||
|
||||
var datastore;
|
||||
// Datastore name declared on the manifest.webapp
|
||||
var DATASTORE_NAME = 'Gaia_Facebook_Friends';
|
||||
// Record Id for the index
|
||||
var INDEX_ID = 1;
|
||||
|
||||
// Indicates the initialization state
|
||||
var readyState = 'notInitialized';
|
||||
// Custom event for notifying initializations
|
||||
var INITIALIZE_EVENT = 'fb_ds_init';
|
||||
|
||||
// Last known revision Id. Used to determine whether the index has to
|
||||
// be reloaded or not
|
||||
var revisionId;
|
||||
|
||||
// Creates the internal Object in the datastore that will act as an index
|
||||
function createIndex() {
|
||||
return {
|
||||
// By tel number and all its possible variants
|
||||
// (We are not supporting dups right now)
|
||||
byTel: Object.create(null),
|
||||
// Prefix tree for enabling searching by partial tel numbers
|
||||
treeTel: []
|
||||
};
|
||||
}
|
||||
|
||||
// The index we need to keep the correspondance between FB Friends and
|
||||
// datastore Ids
|
||||
var index;
|
||||
|
||||
// Ensures a proper error object is returned
|
||||
function safeError(err) {
|
||||
if (err && err.name) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'UnknownError'
|
||||
};
|
||||
}
|
||||
|
||||
function notifyOpenSuccess(cb) {
|
||||
readyState = 'initialized';
|
||||
if (typeof cb === 'function') {
|
||||
window.setTimeout(cb, 0);
|
||||
}
|
||||
var ev = new CustomEvent(INITIALIZE_EVENT);
|
||||
document.dispatchEvent(ev);
|
||||
}
|
||||
|
||||
function initError(outRequest, error) {
|
||||
outRequest.failed(error);
|
||||
}
|
||||
|
||||
function handleInitError(err, errorCb) {
|
||||
readyState = 'error';
|
||||
contacts.error = err;
|
||||
|
||||
if (typeof errorCb === 'function') {
|
||||
window.setTimeout(function() {
|
||||
errorCb(err);
|
||||
}, 0);
|
||||
}
|
||||
// We resume other callers state
|
||||
document.dispatchEvent(new CustomEvent(INITIALIZE_EVENT));
|
||||
}
|
||||
|
||||
|
||||
// Creates a default handler for errors
|
||||
function defaultError(request) {
|
||||
return defaultErrorCb.bind(null, request);
|
||||
}
|
||||
|
||||
// Creates a default handler for success
|
||||
function defaultErrorCb(request, error) {
|
||||
request.failed(error);
|
||||
}
|
||||
|
||||
function setIndex(obj) {
|
||||
index = (obj || createIndex());
|
||||
}
|
||||
|
||||
function successGet(outReq, data) {
|
||||
outReq.done(data);
|
||||
}
|
||||
|
||||
function errorGet(outReq, uid, err) {
|
||||
window.console.error('Error while getting object for UID: ', uid);
|
||||
outReq.failed(err.name);
|
||||
}
|
||||
|
||||
Object.defineProperty(contacts, 'datastore', {
|
||||
get: function getDataStore() { return datastore; },
|
||||
enumerable: false,
|
||||
configurable: false
|
||||
});
|
||||
|
||||
Object.defineProperty(contacts, 'dsIndex', {
|
||||
get: function getIndex() { return index; },
|
||||
set: setIndex,
|
||||
enumerable: false,
|
||||
configurable: false
|
||||
});
|
||||
|
||||
/**
|
||||
* Allows to obtain the FB friend information by UID
|
||||
*
|
||||
*
|
||||
*/
|
||||
contacts.get = function(uid) {
|
||||
var outRequest = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function get() {
|
||||
contacts.init(function() {
|
||||
doGet(uid, outRequest);
|
||||
}, function(err) {
|
||||
initError(outRequest, err);
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return outRequest;
|
||||
};
|
||||
|
||||
function doGet(uid, outRequest) {
|
||||
var successCb = successGet.bind(null, outRequest);
|
||||
var errorCb = errorGet.bind(null, outRequest, uid);
|
||||
|
||||
datastore.get(uid).then(successCb, errorCb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to get FB information by tel number (short or long)
|
||||
* The tel number must be complete
|
||||
*
|
||||
*/
|
||||
contacts.getByPhone = function(tel) {
|
||||
var outRequest = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function get_by_phone() {
|
||||
contacts.init(function get_by_phone() {
|
||||
doGetByPhone(tel, outRequest);
|
||||
},
|
||||
function(err) {
|
||||
initError(outRequest, err);
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return outRequest;
|
||||
};
|
||||
|
||||
function doGetByPhone(tel, outRequest) {
|
||||
var dsId;
|
||||
var normalizedNumber = navigator.mozPhoneNumberService.normalize(tel);
|
||||
if (datastore.revisionId !== revisionId) {
|
||||
window.console.info('Datastore revision id has changed!');
|
||||
// Refreshing the index just in case
|
||||
datastore.get(INDEX_ID).then(function success(obj) {
|
||||
setIndex(obj);
|
||||
revisionId = datastore.revisionId;
|
||||
dsId = index.byTel[normalizedNumber];
|
||||
|
||||
if (typeof dsId !== 'undefined') {
|
||||
datastore.get(dsId).then(function success(friend) {
|
||||
outRequest.done(friend);
|
||||
}, defaultError(outRequest));
|
||||
}
|
||||
else {
|
||||
outRequest.done(null);
|
||||
}
|
||||
}, function(err) {
|
||||
err = safeError(err);
|
||||
window.console.error('The index cannot be refreshed: ', err.name);
|
||||
outRequest.failed(err);
|
||||
});
|
||||
}
|
||||
else {
|
||||
dsId = index.byTel[normalizedNumber];
|
||||
if (typeof dsId !== 'undefined') {
|
||||
datastore.get(dsId).then(function success(friend) {
|
||||
outRequest.done(friend);
|
||||
}, defaultError(outRequest));
|
||||
}
|
||||
else {
|
||||
outRequest.done(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to search for friends by different criteria.
|
||||
* Numbers can be partial and partial matchings will be found
|
||||
*
|
||||
* by === tel : By Tel number
|
||||
*
|
||||
*/
|
||||
contacts.search = function(by, number) {
|
||||
var outRequest = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function do_search() {
|
||||
contacts.init(function do_search_init() {
|
||||
if (by === 'phone') {
|
||||
doSearchByPhone(number, outRequest);
|
||||
}
|
||||
},
|
||||
function(err) {
|
||||
initError(outRequest, err);
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return outRequest;
|
||||
};
|
||||
|
||||
|
||||
function doSearchByPhone(number, outRequest) {
|
||||
var normalizedNumber = navigator.mozPhoneNumberService.normalize(number);
|
||||
LazyLoader.load(['/shared/js/fb/fb_tel_index.js',
|
||||
'/shared/js/binary_search.js'], function() {
|
||||
var toSearchNumber = normalizedNumber;
|
||||
// TODO: Temporal way of searching for international numbers
|
||||
// A follow-up is needed by using PhoneNumber.js exposed to Gaia
|
||||
if (number.charAt(0) === '+') {
|
||||
toSearchNumber = number.substring(1);
|
||||
}
|
||||
if (datastore.revisionId !== revisionId) {
|
||||
window.console.info('Datastore revision id has changed!');
|
||||
// Refreshing the index
|
||||
datastore.get(INDEX_ID).then(function success(obj) {
|
||||
|
||||
setIndex(obj);
|
||||
revisionId = datastore.revisionId;
|
||||
var results = TelIndexer.search(index.treeTel, toSearchNumber);
|
||||
var out = null;
|
||||
if (results.length > 0) {
|
||||
out = datastore.get.apply(datastore, results);
|
||||
}
|
||||
return out;
|
||||
},function(err) {
|
||||
err = safeError(err);
|
||||
window.console.error('The index cannot be refreshed: ', err.name);
|
||||
outRequest.failed(err);
|
||||
}).then(function success(objList) {
|
||||
if (objList && !Array.isArray(objList)) {
|
||||
objList = [objList];
|
||||
}
|
||||
else {
|
||||
objList = [];
|
||||
}
|
||||
outRequest.done(objList);
|
||||
}, function error(err) {
|
||||
err = safeError(err);
|
||||
window.console.error('Error while retrieving result data: ',
|
||||
err.name);
|
||||
outRequest.failed(err);
|
||||
});
|
||||
}
|
||||
else {
|
||||
var results = TelIndexer.search(index.treeTel, toSearchNumber);
|
||||
if (results.length > 0) {
|
||||
datastore.get.apply(datastore, results).then(
|
||||
function success(objList) {
|
||||
if (!Array.isArray(objList)) {
|
||||
objList = [objList];
|
||||
}
|
||||
outRequest.done(objList);
|
||||
}, function error(err) {
|
||||
err = safeError(err);
|
||||
window.console.error('Error while retrieving result data: ',
|
||||
err.name);
|
||||
outRequest.failed(err);
|
||||
});
|
||||
}
|
||||
else {
|
||||
outRequest.done(results);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the index data
|
||||
*
|
||||
*/
|
||||
contacts.refresh = function() {
|
||||
var outRequest = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function clear() {
|
||||
contacts.init(function() {
|
||||
doRefresh(outRequest);
|
||||
},
|
||||
function(err) {
|
||||
initError(outRequest, err);
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return outRequest;
|
||||
};
|
||||
|
||||
function doRefresh(outRequest) {
|
||||
datastore.get(INDEX_ID).then(function success(obj) {
|
||||
setIndex(obj);
|
||||
outRequest.done();
|
||||
}, defaultError(outRequest));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to obtain how many records contain he FB Database
|
||||
*
|
||||
*/
|
||||
contacts.getLength = function() {
|
||||
var retRequest = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function() {
|
||||
contacts.init(function get_all() {
|
||||
doGetLength(retRequest);
|
||||
},
|
||||
function(err) {
|
||||
initError(retRequest, err);
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return retRequest;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Allows to return the total number of records in the DataStore (minus 1)
|
||||
* That's because the index object also counts
|
||||
*
|
||||
*/
|
||||
function doGetLength(outRequest) {
|
||||
datastore.getLength().then(function success(length) {
|
||||
outRequest.done(length - 1);
|
||||
},
|
||||
function error(err) {
|
||||
outRequest.failed(safeError(err));
|
||||
});
|
||||
}
|
||||
|
||||
// Needed only for testing purposes
|
||||
contacts.restart = function() {
|
||||
readyState = 'notInitialized';
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialization function
|
||||
*
|
||||
*/
|
||||
contacts.init = function(cb, errorCb) {
|
||||
if (!navigator.getDataStores) {
|
||||
handleInitError({ name: 'DatastoreNotFound' }, errorCb);
|
||||
return;
|
||||
}
|
||||
|
||||
if (readyState === 'initialized') {
|
||||
cb();
|
||||
return;
|
||||
}
|
||||
|
||||
if (readyState === 'initializing') {
|
||||
document.addEventListener(INITIALIZE_EVENT, function oninitalized() {
|
||||
cb();
|
||||
document.removeEventListener(INITIALIZE_EVENT, oninitalized);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
readyState = 'initializing';
|
||||
|
||||
navigator.getDataStores(DATASTORE_NAME).then(function success(ds) {
|
||||
if (ds.length < 1) {
|
||||
handleInitError({ name: 'DatastoreNotFound' }, errorCb);
|
||||
return;
|
||||
}
|
||||
|
||||
datastore = ds[0];
|
||||
// Checking the length as the index should be there
|
||||
datastore.getLength().then(function(length) {
|
||||
if (length === 0 && datastore.readOnly === false) {
|
||||
window.console.info('Adding index as length is 0');
|
||||
setIndex(createIndex());
|
||||
return datastore.add(index);
|
||||
}
|
||||
else if (length > 0) {
|
||||
return datastore.get(INDEX_ID);
|
||||
}
|
||||
else if (datastore.readOnly === true) {
|
||||
window.console.warn('The datastore is empty and readonly');
|
||||
}
|
||||
return null;
|
||||
}).then(function add_index_success(v) {
|
||||
// Index is created in order not to cause errors
|
||||
if (typeof v !== 'number') {
|
||||
setIndex(v);
|
||||
}
|
||||
revisionId = datastore.revisionId;
|
||||
notifyOpenSuccess(cb);
|
||||
}, function add_index_error(err) {
|
||||
err = safeError(err);
|
||||
window.console.error('Error while setting the index: ', err.name);
|
||||
handleInitError(err, errorCb);
|
||||
});
|
||||
}, function error(err) {
|
||||
err = safeError(err);
|
||||
window.console.error('FB: Error while opening the DataStore: ',
|
||||
err.name);
|
||||
handleInitError(err, errorCb);
|
||||
});
|
||||
};
|
||||
})();
|
||||
39
assets/webapps/ftu.gaiamobile.org/shared/js/fb/fb_request.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
'use strict';
|
||||
|
||||
var fb = self.fb || {};
|
||||
self.fb = fb;
|
||||
|
||||
// Only will be executed in the case of not loading fb.utils previously
|
||||
// i.e. dialer and call log FB integration
|
||||
fb.utils = self.fb.utils || {};
|
||||
self.fb.utils = fb.utils;
|
||||
|
||||
if (typeof fb.utils.Request !== 'function') {
|
||||
/**
|
||||
* Request auxiliary object to support asynchronous calls
|
||||
*
|
||||
*/
|
||||
fb.utils.Request = function() {
|
||||
this.done = function(result) {
|
||||
this.result = result;
|
||||
if (typeof this.onsuccess === 'function') {
|
||||
var ev = {};
|
||||
ev.target = this;
|
||||
window.setTimeout(function() {
|
||||
this.onsuccess(ev);
|
||||
}.bind(this), 0);
|
||||
}
|
||||
};
|
||||
|
||||
this.failed = function(error) {
|
||||
this.error = error;
|
||||
if (typeof this.onerror === 'function') {
|
||||
var ev = {};
|
||||
ev.target = this;
|
||||
window.setTimeout(function() {
|
||||
this.onerror(ev);
|
||||
}.bind(this), 0);
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
279
assets/webapps/ftu.gaiamobile.org/shared/js/fb/fb_tel_index.js
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
'use strict';
|
||||
|
||||
/* globals utils */
|
||||
/* exported TelIndexer */
|
||||
|
||||
// This object implements a prefix tree (aka trie) for FB tel numbers
|
||||
|
||||
function TrieNode(number) {
|
||||
this.keys = Object.create(null);
|
||||
this.leaves = null;
|
||||
this.value = number;
|
||||
}
|
||||
|
||||
var TelIndexer = {
|
||||
_MIN_TEL_LENGTH: 3,
|
||||
_INSERT_MODE: 'insert',
|
||||
|
||||
_indexOf: function(array, value, byPrefix) {
|
||||
var out = -1;
|
||||
|
||||
if (!Array.isArray(array)) {
|
||||
return out;
|
||||
}
|
||||
|
||||
for (var j = 0, l = array.length; j < l; j++) {
|
||||
if (array[j].value === value || (byPrefix &&
|
||||
array[j].value.startsWith(value))) {
|
||||
out = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
},
|
||||
|
||||
_getStringVariants: function(number) {
|
||||
var out = [];
|
||||
|
||||
var limit = number.length - this._MIN_TEL_LENGTH + 1;
|
||||
for (var j = 0; j < limit; j++) {
|
||||
out.push(number.substr(j));
|
||||
}
|
||||
return out;
|
||||
},
|
||||
|
||||
// Allows to index the number passed as parameter
|
||||
index: function(tree, number, dsId) {
|
||||
var self = this;
|
||||
var variants = this._getStringVariants(number);
|
||||
|
||||
variants.forEach(function(aVariant) {
|
||||
self._insert(tree, aVariant, dsId);
|
||||
});
|
||||
},
|
||||
|
||||
// Inserts a substring in a node. dsId is the object id (uid) associated
|
||||
_insertInNode: function(node, str, dsId) {
|
||||
if (str.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var currentStr = str;
|
||||
var insertPointFound = false;
|
||||
var insertObj;
|
||||
var pointerOriginal = str.length;
|
||||
while (!insertPointFound && currentStr.length > 0) {
|
||||
var inextObj = this._indexOf(node.leaves, currentStr, 'prefix');
|
||||
if (inextObj !== -1) {
|
||||
insertPointFound = true;
|
||||
insertObj = node.leaves[inextObj];
|
||||
}
|
||||
else {
|
||||
currentStr = currentStr.substring(0, currentStr.length - 1);
|
||||
}
|
||||
pointerOriginal--;
|
||||
}
|
||||
|
||||
if (insertPointFound) {
|
||||
if (insertObj.value === str) {
|
||||
// Exact insertion point
|
||||
insertObj.keys[dsId] = true;
|
||||
}
|
||||
else {
|
||||
var keys = Object.keys(insertObj.keys);
|
||||
// Let's check what's the gap
|
||||
var newString = insertObj.value.substring(0, currentStr.length);
|
||||
var restString = insertObj.value.substring(currentStr.length);
|
||||
|
||||
insertObj.value = newString;
|
||||
|
||||
if (restString.length > 0) {
|
||||
var newNode = new TrieNode(restString);
|
||||
for (var j = 0; j < keys.length; j++) {
|
||||
newNode.keys[keys[j]] = true;
|
||||
}
|
||||
if (Array.isArray(insertObj.leaves)) {
|
||||
newNode.leaves = [];
|
||||
insertObj.leaves.forEach(function(aLeave) {
|
||||
newNode.leaves.push(aLeave);
|
||||
});
|
||||
}
|
||||
insertObj.leaves = [];
|
||||
insertObj.leaves.push(newNode);
|
||||
}
|
||||
insertObj.keys[dsId] = true;
|
||||
|
||||
var restOriginal = str.substring(pointerOriginal + 1);
|
||||
// And the rest of the original string is inserted in the insertObj
|
||||
this._insertInNode(insertObj, restOriginal, dsId);
|
||||
}
|
||||
}
|
||||
else {
|
||||
var nextObj = new TrieNode(str);
|
||||
nextObj.keys[dsId] = true;
|
||||
node.leaves = node.leaves || [];
|
||||
node.leaves.push(nextObj);
|
||||
}
|
||||
},
|
||||
|
||||
// Compare function used for binary search
|
||||
_compare: function(a, b) {
|
||||
return a.localeCompare(b);
|
||||
},
|
||||
|
||||
// Find the first level node (corresponding to the 3 first numbers)
|
||||
_getFirstLevelNode: function(tree, str, mode) {
|
||||
var out = null;
|
||||
|
||||
var index;
|
||||
if (mode === this._INSERT_MODE) {
|
||||
index = this._indexOf(tree, str);
|
||||
if (index !== -1) {
|
||||
out = tree[index];
|
||||
}
|
||||
}
|
||||
else {
|
||||
index = utils.binarySearch(str, tree, {
|
||||
compareFunction: this._compare,
|
||||
arrayField: 'value'
|
||||
});
|
||||
|
||||
if (index.length > 0) {
|
||||
out = tree[index[0]];
|
||||
}
|
||||
}
|
||||
return out;
|
||||
},
|
||||
|
||||
// Orders the tree enabling binary search on the first levels
|
||||
orderTree: function(tree) {
|
||||
tree.sort(function(a, b) {
|
||||
return a.value.localeCompare(b.value);
|
||||
});
|
||||
},
|
||||
|
||||
// Inserts a number on the tree
|
||||
_insert: function(tree, str, dsId) {
|
||||
var firstThreeStr = str.substring(0, this._MIN_TEL_LENGTH);
|
||||
var rootObj = this._getFirstLevelNode(tree, firstThreeStr,
|
||||
this._INSERT_MODE);
|
||||
if (!rootObj) {
|
||||
rootObj = new TrieNode(firstThreeStr);
|
||||
tree.push(rootObj);
|
||||
}
|
||||
rootObj.keys[dsId] = true;
|
||||
|
||||
this._insertInNode(rootObj, str.substring(this._MIN_TEL_LENGTH), dsId);
|
||||
},
|
||||
|
||||
// Search a substring in a node
|
||||
_searchInNode: function(node, str) {
|
||||
var currentStr = str;
|
||||
var found = false;
|
||||
var pointerOriginal = str.length;
|
||||
var searchObj;
|
||||
while (!found && currentStr.length > 0) {
|
||||
var idxObj = this._indexOf(node.leaves, currentStr, 'prefix');
|
||||
if (idxObj !== -1) {
|
||||
found = true;
|
||||
searchObj = node.leaves[idxObj];
|
||||
}
|
||||
else {
|
||||
currentStr = currentStr.substring(0, currentStr.length - 1);
|
||||
}
|
||||
pointerOriginal--;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
if (searchObj.value === str) {
|
||||
return searchObj;
|
||||
}
|
||||
// Let's calculate what is the gap
|
||||
var restString = str.substr(pointerOriginal + 1);
|
||||
if (restString.length > 0) {
|
||||
return this._searchInNode(searchObj, restString);
|
||||
}
|
||||
else {
|
||||
return searchObj;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
// Search for a number (which can be partial) on the tree
|
||||
search: function(tree, number) {
|
||||
var out = [];
|
||||
var totalLength = number.length;
|
||||
var MIN_TEL_LENGTH = this._MIN_TEL_LENGTH;
|
||||
|
||||
if (totalLength >= MIN_TEL_LENGTH) {
|
||||
var firstThreeStr = number.substring(0, MIN_TEL_LENGTH);
|
||||
var rootObj = this._getFirstLevelNode(tree, firstThreeStr);
|
||||
|
||||
if (rootObj) {
|
||||
if (totalLength === MIN_TEL_LENGTH) {
|
||||
out = Object.keys(rootObj.keys);
|
||||
}
|
||||
else {
|
||||
var node = this._searchInNode(rootObj,
|
||||
number.substring(MIN_TEL_LENGTH));
|
||||
if (node) {
|
||||
out = Object.keys(node.keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
},
|
||||
|
||||
// Removes a number from the tree
|
||||
remove: function(tree, number, dsId) {
|
||||
var self = this;
|
||||
var variants = this._getStringVariants(number);
|
||||
variants.forEach(function(aVariant) {
|
||||
self._unIndex(tree, aVariant, dsId);
|
||||
});
|
||||
},
|
||||
|
||||
// Removes a partial string from a node
|
||||
_removeInNode: function(node, str, dsId) {
|
||||
if (str.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var currentStr = str;
|
||||
var found = false;
|
||||
var pointerOriginal = str.length;
|
||||
var searchObj;
|
||||
while (!found && currentStr.length > 0) {
|
||||
var idxObj = this._indexOf(node.leaves, currentStr, 'prefix');
|
||||
if (idxObj !== -1) {
|
||||
found = true;
|
||||
searchObj = node.leaves[idxObj];
|
||||
}
|
||||
else {
|
||||
currentStr = currentStr.substring(0, currentStr.length - 1);
|
||||
}
|
||||
pointerOriginal--;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
delete searchObj.keys[dsId];
|
||||
this._removeInNode(searchObj, str.substr(pointerOriginal + 1), dsId);
|
||||
}
|
||||
},
|
||||
|
||||
// Removes a number from the tree
|
||||
// TODO: Compact the tree when a number is removed
|
||||
_unIndex: function(tree, number, dsId) {
|
||||
var firstThreeStr = number.substring(0, this._MIN_TEL_LENGTH);
|
||||
|
||||
var rootObj = this._getFirstLevelNode(tree, firstThreeStr);
|
||||
if (rootObj) {
|
||||
delete rootObj.keys[dsId];
|
||||
this._removeInNode(rootObj, number.substr(this._MIN_TEL_LENGTH), dsId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
;'use strict';var FxAccountsIACHelper=function FxAccountsIACHelper(){var DEFAULT_CONNECTION_STRING='fxa-mgmt';var default_rules={'manifestURLs':['app://system.gaiamobile.org/manifest.webapp','app://smart-system.gaiamobile.org/manifest.webapp']};var CONNECTION_STRING=DEFAULT_CONNECTION_STRING;var rules=default_rules;var port;var callbacks={};var eventListeners={};var init=function init(options){if(!options){return;}
|
||||
if(options.keyword&&typeof(options.keyword)=='string'&&options.keyword.length>0){CONNECTION_STRING=options.keyword;}
|
||||
if(options.rules&&typeof(options.rules)=='object'){rules=options.rules;}};var addEventListener=function addEventListener(eventName,listener){if(!eventListeners[eventName]){eventListeners[eventName]=[];}
|
||||
eventListeners[eventName].push(listener);};var removeEventListener=function removeEventListener(eventName,listener){var listeners=eventListeners[eventName];if(!listeners){return;}
|
||||
var index=listeners.indexOf(listener);if(index===-1){return;}
|
||||
listeners.splice(index,1);};var reset=function reset(){CONNECTION_STRING=DEFAULT_CONNECTION_STRING;rules=default_rules;eventListeners={};callbacks={};port=null;};var onMessage=function onMessage(evt){if(evt&&evt.data){var message=evt.data;if(message.eventName){var listeners=eventListeners[message.eventName];if(!listeners){return;}
|
||||
for(var listener in listeners){if(listeners[listener]&&typeof listeners[listener]==='function'){listeners[listener]();}}
|
||||
return;}
|
||||
var cbs,cb;if(message.methodName){cbs=callbacks[message.methodName];if(!cbs||!cbs.length){console.warn('No callbacks for method '+message.methodName);return;}
|
||||
while(cbs.length){cb=cbs.shift();if(!message.error){cb.successCb(message.data);}else{cb.errorCb(message.error);}}}}else{console.error('Unknown');}};var getSelf=function getSelf(cb,error){var request=navigator.mozApps.getSelf();request.onsuccess=function onSuccess(evt){cb(evt.target.result);};request.onerror=function onError(){if(error&&typeof(error)==='function'){error();}};};var requestQueue=[];var isConnecting=false;var connect=function connect(cb){if(isConnecting){return requestQueue.push(cb);}
|
||||
isConnecting=true;_connect(function onConnect(err){var next;isConnecting=false;if(typeof cb==='function'){cb(err);}
|
||||
while(!!(next=requestQueue.shift())){next&&next(err);}});};var _connect=function _connect(callback){getSelf(function onApp(app){if(!app){return;}
|
||||
app.connect(CONNECTION_STRING,rules).then(function(ports){if(!ports||ports.length!==1){return;}
|
||||
port=ports[0];callback&&callback();}).catch(function(error){callback&&callback(error);});});};var sendMessage=function sendMessage(message,successCb,errorCb){if(port){_sendMessage(message,successCb,errorCb);return;}
|
||||
connect(function(error){if(error){errorCb&&errorCb(error);return;}_sendMessage(message,successCb,errorCb);});};var _sendMessage=function _sendMessage(message,successCb,errorCb){var name=message.name;if(!name){return;}
|
||||
if(!callbacks[name]){callbacks[name]=[];}
|
||||
callbacks[name].push({successCb:successCb,errorCb:errorCb});port.onmessage=onMessage;port.postMessage(message);};var getAccount=function getAccount(successCb,errorCb){sendMessage({'name':'getAccount'},successCb,errorCb);};var openFlow=function openFlow(successCb,errorCb){sendMessage({'name':'openFlow'},successCb,errorCb);};var logout=function logout(successCb,errorCb){sendMessage({'name':'logout'},successCb,errorCb);};var refreshAuthentication=function refreshAuthentication(email,successCb,errorCb){sendMessage({'name':'refreshAuthentication','email':email},successCb,errorCb);};var resendVerificationEmail=function resendVerificationEmail(email,successCb,errorCb){sendMessage({'name':'resendVerificationEmail','email':email},successCb,errorCb);};connect();return{'addEventListener':addEventListener,'getAccount':getAccount,'init':init,'logout':logout,'openFlow':openFlow,'refreshAuthentication':refreshAuthentication,'removeEventListener':removeEventListener,'resendVerificationEmail':resendVerificationEmail,'reset':reset};}();
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
;'use strict';(function(exports){var _iccManager=navigator.mozIccManager;var _iccProxy=null;Object.defineProperty(exports,'IccHelper',{configurable:true,get:function(){return _iccProxy;},set:function(value){_iccProxy=value;}});if(_iccManager&&_iccManager.getIccById){var activeIccObj=null;var eventListeners={};var cachedEventListeners={};var events=['cardstatechange','iccinfochange','stkcommand','stksessionend'];var getters=['iccInfo','cardState'];var methods=['sendStkResponse','sendStkMenuSelection','sendStkTimerExpiration','sendStkEventDownload'];var domRequests=['getCardLock','unlockCardLock','setCardLock','getCardLockRetryCount','readContacts','updateContact','iccOpenChannel','iccExchangeAPDU','iccCloseChannel'];var getterTemplate=function(name){return function(){if(activeIccObj){return activeIccObj[name];}else{return null;}};};var methodTemplate=function(name){return function(){if(activeIccObj){return activeIccObj[name].apply(activeIccObj,arguments);}};};var createIccProxy=function(){var iccProxy={addEventListener:function(eventName,callback){if(typeof callback!=='function'){return;}
|
||||
if(events.indexOf(eventName)===-1){return;}
|
||||
var listeners=eventListeners[eventName];if(listeners==null){listeners=eventListeners[eventName]=[];}
|
||||
if(listeners.indexOf(callback)===-1){listeners.push(callback);}},removeEventListener:function(eventName,callback){var listeners=eventListeners[eventName];if(listeners){var index=listeners.indexOf(callback);if(index!==-1){listeners.splice(index,1);}}}};getters.forEach(function(getter){Object.defineProperty(iccProxy,getter,{enumerable:true,get:getterTemplate(getter)});});methods.forEach(function(method){iccProxy[method]=methodTemplate(method);});domRequests.forEach(function(request){iccProxy[request]=methodTemplate(request);});events.forEach(function(eventName){Object.defineProperty(iccProxy,'on'+eventName,{enumerable:true,set:function(newListener){var oldListener=cachedEventListeners[eventName];if(oldListener){iccProxy.removeEventListener(eventName,oldListener);}
|
||||
cachedEventListeners[eventName]=newListener;if(newListener){iccProxy.addEventListener(eventName,newListener);}},get:function(){return cachedEventListeners[eventName];}});});return iccProxy;};_iccProxy=createIccProxy();if(_iccManager.iccIds&&_iccManager.iccIds.length){activeIccObj=_iccManager.getIccById(_iccManager.iccIds[0]);if(activeIccObj){events.forEach(function(eventName){activeIccObj.addEventListener(eventName,function(event){var listeners=eventListeners[eventName];if(listeners){listeners.forEach(function(listener){listener(event);});}});});}}
|
||||
_iccManager.oniccdetected=function(event){if(_iccProxy.cardState==null){activeIccObj=_iccManager.getIccById(event.iccId);if(activeIccObj){events.forEach(function(eventName){activeIccObj.addEventListener(eventName,function(event){var listeners=eventListeners[eventName];if(listeners){listeners.forEach(function(listener){listener(event);});}});});['cardstatechange','iccinfochange'].forEach(function(eventName){if(eventListeners[eventName]){eventListeners[eventName].forEach(function(listener){listener();});}});}}};}else{_iccProxy=_iccManager;}})(window);
|
||||
32
assets/webapps/ftu.gaiamobile.org/shared/js/icons_helper.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
;'use strict';(function IconsHelper(exports){const ICON_CACHE_PERIOD=24*60*60*1000;const FETCH_XHR_TIMEOUT=10000;const DEBUG=false;var dataStore=null;function getDefaultIconSize(){var dpr=window.devicePixelRatio;return(dpr&&dpr>1)?142:84;}
|
||||
function sizeIsNearer(size1,size2,targetSize){var delta1=Math.abs(targetSize-size1);var delta2=Math.abs(targetSize-size2);return(delta1<=delta2);}
|
||||
function getIcon(uri,iconTargetSize,placeObj={},siteObj={}){var iconUrl=null;iconTargetSize=iconTargetSize*window.devicePixelRatio;if(siteObj.webManifestUrl&&siteObj.webManifest){iconUrl=getBestIconFromWebManifest(siteObj.webManifest,iconTargetSize);if(DEBUG&&iconUrl){console.log('Icon from Web Manifest');}}
|
||||
if(!iconUrl&&siteObj.manifest&&siteObj.manifest.icons){iconUrl=getBestIconFromWebManifest({icons:_convertToWebManifestIcons(siteObj.manifest,siteObj.origin||siteObj.manifest.origin)},iconTargetSize);if(DEBUG&&iconUrl){console.log('Icon from Firefox App Manifest');}}
|
||||
if(!iconUrl&&placeObj&&placeObj.icons){iconUrl=getBestIconFromMetaTags(placeObj.icons,iconTargetSize);if(DEBUG&&iconUrl){console.log('Icon from Meta tags');}}
|
||||
if(!iconUrl){var a=document.createElement('a');a.href=uri;iconUrl=a.origin+'/favicon.ico';if(iconTargetSize){iconUrl+='#-moz-resolution='+iconTargetSize+','+iconTargetSize;}
|
||||
DEBUG&&console.log('Icon from favicon.ico');}
|
||||
return new Promise(resolve=>{resolve(iconUrl);});}
|
||||
function getIconBlob(uri,iconTargetSize,placeObj={},siteObj={}){return new Promise((resolve,reject)=>{getIcon(uri,iconTargetSize,placeObj,siteObj).then(iconUrl=>{getStore().then(iconStore=>{iconStore.get(iconUrl).then(iconObj=>{if(!iconObj||!iconObj.timestamp||Date.now()-iconObj.timestamp>=ICON_CACHE_PERIOD){return fetchIconBlob(iconUrl).then(iconBlob=>{var img=document.createElement('img');var icon=new Icon(img,uri);icon.renderBlob(iconBlob,{size:iconTargetSize,onLoad:function(blob){var iconObj={blob:blob,originalUrl:iconUrl.toString(),timestamp:Date.now()};resolve(iconObj);iconStore.add(iconObj,iconUrl);},onerror:function(e){reject(`Failed to fetch icon ${iconUrl}`);}});}).catch(err=>{reject(`Failed to fetch icon ${iconUrl}: ${err}`);});}
|
||||
return resolve(iconObj);}).catch(err=>{reject(`Failed to get icon from dataStore: ${err}`);});}).catch(err=>{reject(`Error opening the dataStore: ${err}`);});});});}
|
||||
function setElementIcon(icon,targetSize){return getIconBlob(icon.bookmark.url,targetSize,icon.bookmark,icon.bookmark).then(iconObj=>{if(iconObj.blob){icon.icon=iconObj.blob;return Promise.resolve();}else if(icon.bookmark.icon){return fetchIconBlob(icon.bookmark.icon).then(iconBlob=>{icon.icon=iconBlob;return Promise.resolve();},Promise.reject.bind(Promise));}
|
||||
return Promise.reject('No icon data found');},Promise.reject.bind(Promise));}
|
||||
function getBestIconFromWebManifest(webManifest,iconSize){var icons=webManifest.icons;if(!icons){return null;}
|
||||
var maxSize=10000;var bestSize=maxSize;var iconURL=null;iconSize=iconSize||getDefaultIconSize();icons.forEach((potentialIcon)=>{if(!iconURL){iconURL=potentialIcon.src;}
|
||||
var sizes=Array.from(potentialIcon.sizes);var nearestSize=getNearestSize(sizes,iconSize,bestSize);if(nearestSize!==bestSize&&sizeIsNearer(nearestSize,bestSize,iconSize)){iconURL=potentialIcon.src;bestSize=nearestSize;}});return iconURL?iconURL:null;}
|
||||
function _convertToWebManifestIcons(manifest,origin){return Object.keys(manifest.icons).map(function(size){var url=manifest.icons[size];var sizes=[size+'x'+size];url=url.indexOf('http')>-1?url:origin+url;return{src:new URL(url),sizes:sizes};});}
|
||||
function getBestIconFromMetaTags(icons,iconSize){if(!icons){return null;}
|
||||
iconSize=iconSize||getDefaultIconSize();var iconURL=null;var bestSize=10000;Object.keys(icons).forEach((uri)=>{var potentialIcon=icons[uri];if(!iconURL){iconURL=uri;}
|
||||
var sizes=Array.from(potentialIcon.sizes);var nearestSize=getNearestSize(sizes,iconSize,bestSize);if(nearestSize!==bestSize&&sizeIsNearer(nearestSize,bestSize,iconSize)){iconURL=uri;bestSize=nearestSize;}
|
||||
if(potentialIcon.rel==='apple-touch-icon'||potentialIcon.rel==='apple-touch-icon-precomposed'){var moreInfoUrl='https://developer.mozilla.org/en-US/'+'Apps/Build/Icon_implementation_for_apps#General_icons_for_web_apps';console.warn('Warning: The apple-touch icons are being used '+'as a fallback only. They will be deprecated in '+'the future. See '+moreInfoUrl);}});return iconURL||null;}
|
||||
function getNearestSize(sizes,iconSize,bestSize){var bogusSize=10000;if(!bestSize){bestSize=bogusSize;}
|
||||
var nearestSize=sizes.reduce(function(nearestSize,sizeString,idx){var size=widthFromSizeString(sizeString);if(isNaN(size)){return nearestSize;}
|
||||
if(sizeIsNearer(size,nearestSize,iconSize)){return size;}else{return nearestSize;}},bestSize);return nearestSize===bogusSize?-1:nearestSize;}
|
||||
function widthFromSizeString(size){size=size||'';var xIndex=size.indexOf('x');if(!xIndex){return NaN;}
|
||||
return parseInt(size.substr(0,xIndex));}
|
||||
function getStore(){return new Promise(resolve=>{if(dataStore){return resolve(dataStore);}
|
||||
navigator.getDataStores('icons').then(stores=>{dataStore=stores[0];return resolve(dataStore);});});}
|
||||
function clear(){return getStore().then(iconStore=>{iconStore.clear();});}
|
||||
function fetchIcon(iconUrl){return new Promise((resolve,reject)=>{fetchIconBlob(iconUrl).then((iconBlob)=>{var img=document.createElement('img');img.src=URL.createObjectURL(iconBlob);img.onload=()=>{var iconSize=Math.max(img.naturalWidth,img.naturalHeight);resolve({blob:iconBlob,url:iconUrl,size:iconSize,timestamp:Date.now()});};img.onerror=()=>{reject(new Error(`Error while loading image.`));};}).catch((e)=>{reject(new Error(`Error while loading image: ${e}`));});});}
|
||||
function fetchIconBlob(iconUrl){return new Promise((resolve,reject)=>{var xhr=new XMLHttpRequest({mozAnon:true,mozSystem:true});xhr.open('GET',iconUrl,true);xhr.responseType='blob';xhr.timeout=FETCH_XHR_TIMEOUT;xhr.send();xhr.onload=()=>{if(xhr.readyState===XMLHttpRequest.DONE&&xhr.status===200){var iconBlob=xhr.response;resolve(iconBlob);return;}
|
||||
reject(new Error(`Got HTTP status ${xhr.status} trying to load ${iconUrl}.`));};xhr.onerror=xhr.ontimeout=()=>{reject(new Error(`Error while getting ${iconUrl}.`));};});}
|
||||
exports.IconsHelper={getIcon:getIcon,getIconBlob:getIconBlob,setElementIcon:setElementIcon,getBestIconFromWebManifest:getBestIconFromWebManifest,getBestIconFromMetaTags:getBestIconFromMetaTags,fetchIcon:fetchIcon,fetchIconBlob:fetchIconBlob,get defaultIconSize(){return getDefaultIconSize();},clear:clear,getNearestSize:getNearestSize,};})(window);
|
||||
572
assets/webapps/ftu.gaiamobile.org/shared/js/image_utils.js
Normal file
|
|
@ -0,0 +1,572 @@
|
|||
/*
|
||||
* This shared module defines a single ImageUtils object in the global scope.
|
||||
*
|
||||
* ImageUtils.getSizeAndType() takes a Blob as its argument and
|
||||
* returns a Promise. If the promise resolves, the resolve method is
|
||||
* passed an object with width, height, and type properties. Width and
|
||||
* height specify the width and height of the image in pixels. Type
|
||||
* specifies the MIME type of the image. If the promise is rejected,
|
||||
* the reject method will be passed a string error message.
|
||||
*
|
||||
* Supported image types are JPEG, PNG, GIF and BMP. The MIME type
|
||||
* strings returned when a promise resolves are "image/jpeg",
|
||||
* "image/png", "image/gif", and "image/bmp".
|
||||
*
|
||||
* If you pass a blob that holds an image of some other type (or a
|
||||
* blob that is not an image at all) then promise will be rejected.
|
||||
*
|
||||
* getSizeAndType() does not decode the image to determine its size
|
||||
* because can require large amounts of memory. Instead, it parses the
|
||||
* image file to determine its size and typically only needs to read a
|
||||
* prefix of the file into memory.
|
||||
*
|
||||
* getSizeAndType() differs from the getImageSize() function defined
|
||||
* in shared/js/media/immage_size.js. That function is similar but
|
||||
* also parses EXIF metadata for JPEG images and is only suitable if
|
||||
* you need to access EXIF data.
|
||||
*/
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
/* global Promise */
|
||||
|
||||
var ImageUtils = exports.ImageUtils = {};
|
||||
|
||||
// Define constants for the MIME types we use
|
||||
const JPEG = 'image/jpeg';
|
||||
const PNG = 'image/png';
|
||||
const GIF = 'image/gif';
|
||||
const BMP = 'image/bmp';
|
||||
|
||||
// Export them
|
||||
ImageUtils.JPEG = JPEG;
|
||||
ImageUtils.PNG = PNG;
|
||||
ImageUtils.GIF = GIF;
|
||||
ImageUtils.BMP = BMP;
|
||||
|
||||
ImageUtils.getSizeAndType = function getSizeAndType(imageBlob) {
|
||||
if (!(imageBlob instanceof Blob)) {
|
||||
console.log('not blob');
|
||||
return Promise.reject(new TypeError('argument is not a Blob'));
|
||||
}
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (imageBlob.size <= 16) {
|
||||
reject('corrupt image file');
|
||||
return;
|
||||
}
|
||||
|
||||
// Start off assuming that we'll read the first 32kb of the file
|
||||
var bytesToRead = 32 * 1024;
|
||||
|
||||
// But if the blob indicates that this is a PNG, GIF, or BMP, then
|
||||
// we can read less since the size information is always close to the
|
||||
// start of the file for those formats.
|
||||
if (imageBlob.type === PNG ||
|
||||
imageBlob.type === GIF ||
|
||||
imageBlob.type === BMP) {
|
||||
bytesToRead = 512;
|
||||
}
|
||||
|
||||
// Try to find the information we want in the first bytes of the file
|
||||
findSizeAndType(imageBlob, bytesToRead, success, tryagain);
|
||||
|
||||
// If we got the size and type data, resolve the promise
|
||||
function success(data) {
|
||||
resolve(data);
|
||||
}
|
||||
|
||||
// If we didn't find it, try again for JPEG files
|
||||
function tryagain(data) {
|
||||
// If we were able to verify that this blob is a jpeg file
|
||||
// then we will try again because in JPEG files, the size
|
||||
// comes after the EXIF data so if there is a large preview image,
|
||||
// the size might be more than 32kb into the file
|
||||
if (data.type === JPEG) {
|
||||
findSizeAndType(imageBlob, imageBlob.size, success, failure);
|
||||
}
|
||||
// For all other known image types the type and size data is
|
||||
// near the start of the file, if we didn't find it on this first
|
||||
// try we need to give up now.
|
||||
else {
|
||||
reject(data.error);
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't find the data even after a second attempt then reject.
|
||||
function failure(data) {
|
||||
reject(data.error);
|
||||
}
|
||||
});
|
||||
|
||||
// This is the internal helper function that actually reads the blob
|
||||
// and finds type and size information.
|
||||
function findSizeAndType(imageBlob, amountToRead, success, failure) {
|
||||
var slice = imageBlob.slice(0, Math.min(amountToRead, imageBlob.size));
|
||||
var reader = new FileReader();
|
||||
reader.readAsArrayBuffer(slice);
|
||||
reader.onloadend = function() {
|
||||
parseImageData(reader.result);
|
||||
};
|
||||
|
||||
function parseImageData(buffer) {
|
||||
var header = new Uint8Array(buffer, 0, 16);
|
||||
var view = new DataView(buffer);
|
||||
|
||||
if (header[0] === 0x89 &&
|
||||
header[1] === 0x50 && /* P */
|
||||
header[2] === 0x4e && /* N */
|
||||
header[3] === 0x47 && /* G */
|
||||
header[4] === 0x0d && /* \r */
|
||||
header[5] === 0x0a && /* \n */
|
||||
header[6] === 0x1A &&
|
||||
header[7] === 0x0a &&
|
||||
header[12] === 0x49 && /* I */
|
||||
header[13] === 0x48 && /* H */
|
||||
header[14] === 0x44 && /* D */
|
||||
header[15] === 0x52) /* R */
|
||||
{
|
||||
// This is a PNG file
|
||||
try {
|
||||
success({
|
||||
type: PNG,
|
||||
width: view.getUint32(16, false), // 32 bit big endian width
|
||||
height: view.getUint32(20, false) // 32 bit big endian height
|
||||
});
|
||||
}
|
||||
catch (ex) {
|
||||
failure({ error: ex.toString()});
|
||||
}
|
||||
}
|
||||
else if (header[0] === 0x47 && /* G */
|
||||
header[1] === 0x49 && /* I */
|
||||
header[2] === 0x46 && /* F */
|
||||
header[3] === 0x38 && /* 8 */
|
||||
(header[4] === 0x37 || /* 7 */
|
||||
header[4] === 0x39) && /* or 9 */
|
||||
header[5] === 0x61) /* a */
|
||||
{
|
||||
// This is a GIF file
|
||||
try {
|
||||
success({
|
||||
type: GIF,
|
||||
width: view.getUint16(6, true), // 16-bit little endian width
|
||||
height: view.getUint16(8, true) // 16-big little endian height
|
||||
});
|
||||
}
|
||||
catch (ex) {
|
||||
failure({ error: ex.toString() });
|
||||
}
|
||||
}
|
||||
else if (header[0] === 0x42 && /* B */
|
||||
header[1] === 0x4D && /* M */
|
||||
view.getUint32(2, true) === imageBlob.size)
|
||||
{
|
||||
// This is a BMP file
|
||||
try {
|
||||
var width, height;
|
||||
|
||||
if (view.getUint16(14, true) === 12) { // check format version
|
||||
width = view.getUint16(18, true); // 16-bit little endian width
|
||||
height = view.getUint16(20, true); // 16-bit little endian height
|
||||
}
|
||||
else { // newer versions of the format use 32-bit ints
|
||||
width = view.getUint32(18, true); // 32-bit little endian width
|
||||
height = view.getUint32(22, true); // 32-bit little endian height
|
||||
}
|
||||
|
||||
success({
|
||||
type: BMP,
|
||||
width: width,
|
||||
height: height
|
||||
});
|
||||
}
|
||||
catch (ex) {
|
||||
failure({ error: ex.toString() });
|
||||
}
|
||||
}
|
||||
else if (header[0] === 0xFF &&
|
||||
header[1] === 0xD8) {
|
||||
var value = {
|
||||
type: JPEG
|
||||
};
|
||||
|
||||
// This is a JPEG file. To find its width and height we need
|
||||
// to skip past the EXIF data and find the start of image data.
|
||||
try {
|
||||
var offset = 2;
|
||||
|
||||
// Loop through the segments of the file until we find an SOF
|
||||
// segment with image size or until we reach the end of our data.
|
||||
for (;;) {
|
||||
// The byte at the current offset should be 0xFF marking
|
||||
// the start of a new segment.
|
||||
if (view.getUint8(offset) !== 0xFF) {
|
||||
failure({ error: 'corrupt JPEG file' });
|
||||
}
|
||||
var segmentType = view.getUint8(offset + 1);
|
||||
var segmentSize = view.getUint16(offset + 2) + 2;
|
||||
|
||||
// If we found an SOF "Start of Frame" segment, we can get
|
||||
// the image size from it.
|
||||
if ((segmentType >= 0xC0 && segmentType <= 0xC3) ||
|
||||
(segmentType >= 0xC5 && segmentType <= 0xC7) ||
|
||||
(segmentType >= 0xC9 && segmentType <= 0xCB) ||
|
||||
(segmentType >= 0xCD && segmentType <= 0xCF))
|
||||
{
|
||||
// 16-bit big-endian dimensions, height first
|
||||
value.height = view.getUint16(offset + 5, false);
|
||||
value.width = view.getUint16(offset + 7, false);
|
||||
success(value);
|
||||
break;
|
||||
}
|
||||
|
||||
// Otherwise, move on to the next segment
|
||||
offset += segmentSize;
|
||||
|
||||
// We may not have read the entire file into our array buffer
|
||||
// so we have to make sure we have not gone past the end of
|
||||
// the buffer before looping again. Note that in this case
|
||||
// the object we pass to the failure callback includes the
|
||||
// image type. This is a signal to getSizeAndType that it
|
||||
// should try again with a larger ArrayBuffer.
|
||||
if (offset + 9 > view.byteLength) {
|
||||
value.error = 'corrupt JPEG file';
|
||||
failure(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ex) {
|
||||
failure({ error: ex.toString() });
|
||||
}
|
||||
}
|
||||
else {
|
||||
failure({ error: 'unknown image type' });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Given a blob containing an image file, this function returns a
|
||||
// Promise that will resolve to a blob for an image file that has
|
||||
// the specified width and height. If the blob already has that
|
||||
// size, it is returned unchanged. If the image is bigger than the
|
||||
// specified size, then it will be shrunk and cropped as necessary
|
||||
// to fit the specified dimensions. If the image is smaller than
|
||||
// the specified size, it will be enlarged and cropped to fit. The
|
||||
// cropping is done in the same way as with the CSS
|
||||
// background-size:cover attribute.
|
||||
//
|
||||
// If the image is resized and outputType is "image/jpeg" or
|
||||
// "image/png" then that type will be used for the resized image. If
|
||||
// a JPEG or PNG image is resized and outputType is not specified,
|
||||
// then the output image will have the same type as the input image.
|
||||
// In all other cases, a resized image will be encoded as a PNG image.
|
||||
// Note that when an image does not need to be resized its type is not
|
||||
// changed, even if outputType is specified.
|
||||
// 'encoderOptions' is passed 'as is' to the 'toBlob' method of canvas:
|
||||
// A Number between 0 and 1 indicating image quality if the requested type
|
||||
// is image/jpeg or image/webp. If this argument is anything else,
|
||||
// the default value for image quality is used.
|
||||
ImageUtils.resizeAndCropToCover = function(inputImageBlob,
|
||||
outputWidth, outputHeight,
|
||||
outputType, encoderOptions)
|
||||
{
|
||||
if (!outputWidth || !isFinite(outputWidth) || outputWidth <= 0 ||
|
||||
!outputHeight || !isFinite(outputHeight) || outputHeight <= 0) {
|
||||
return Promise.reject(new TypeError('invalid output dimensions'));
|
||||
}
|
||||
outputWidth = Math.round(outputWidth);
|
||||
outputHeight = Math.round(outputHeight);
|
||||
|
||||
return ImageUtils.getSizeAndType(inputImageBlob).then(
|
||||
function resolve(data) {
|
||||
var inputWidth = data.width;
|
||||
var inputHeight = data.height;
|
||||
|
||||
// If the image already has the desired size, just return it unchanged
|
||||
if (inputWidth === outputWidth && inputHeight === outputHeight) {
|
||||
return inputImageBlob;
|
||||
}
|
||||
|
||||
// Otherwise, return a promise for the resized image
|
||||
return resize(data);
|
||||
},
|
||||
function reject(error) {
|
||||
// If we couldn't determine the image size and type for some
|
||||
// reason we are still going to assume that it is a valid image,
|
||||
// just one we don't know about. If gecko can decode it we'll
|
||||
// use it and won't reject unless gecko refuses it.
|
||||
return resize({});
|
||||
}
|
||||
);
|
||||
|
||||
// Decode the image in an <img> element, resize and/or crop it into a
|
||||
// <canvas> and the re-encode it to a blob again. Returns a Promise
|
||||
// that resolves to the encoded blob.
|
||||
function resize(data) {
|
||||
var inputType = data.type;
|
||||
var inputWidth = data.width;
|
||||
var inputHeight = data.height;
|
||||
|
||||
// Ignore invalid outputType parameters
|
||||
if (outputType && outputType !== JPEG && outputType !== PNG) {
|
||||
console.warn('Ignoring unsupported outputType', outputType);
|
||||
outputType = undefined;
|
||||
}
|
||||
|
||||
// Determine the output type if none was specified.
|
||||
if (!outputType) {
|
||||
if (inputType === JPEG || inputType === PNG) {
|
||||
outputType = inputType;
|
||||
}
|
||||
else {
|
||||
outputType = PNG;
|
||||
}
|
||||
}
|
||||
|
||||
// Get a URL for the image blob so we can ask gecko to decode it.
|
||||
var url = URL.createObjectURL(inputImageBlob);
|
||||
|
||||
// If we can downsample the image while decoding it, then add a
|
||||
// #-moz-samplesize media fragment to the URL. For large JPEG inputs
|
||||
// this can save megabytes of memory.
|
||||
var mediaFragment;
|
||||
if (inputType === JPEG &&
|
||||
inputWidth > outputWidth && inputHeight > outputHeight) {
|
||||
// How much would we like to scale the image by while decoding?
|
||||
var reduction = Math.max(outputWidth / inputWidth,
|
||||
outputHeight / inputHeight);
|
||||
// Get a media fragment that will downsample the image by up to
|
||||
// this amount.
|
||||
mediaFragment = ImageUtils.Downsample.sizeNoMoreThan(reduction);
|
||||
}
|
||||
else {
|
||||
mediaFragment = '';
|
||||
}
|
||||
|
||||
// The next step is to decode (while possibly downsampling) the
|
||||
// image, but that is asynchronous, so we'll have to return a
|
||||
// Promise to handle that
|
||||
return new Promise(function(resolve, reject) {
|
||||
var offscreenImage = new Image();
|
||||
offscreenImage.src = url + mediaFragment;
|
||||
|
||||
offscreenImage.onerror = function(e) {
|
||||
cleanupImage();
|
||||
reject('failed to decode image');
|
||||
};
|
||||
|
||||
offscreenImage.onload = function() {
|
||||
// The image has been decoded now, so we know its actual size.
|
||||
// If getSizeAndType failed, or if we used a media fragment then
|
||||
// this may be different than the inputWidth and inputHeight
|
||||
// values we used previously.
|
||||
var actualWidth = offscreenImage.width;
|
||||
var actualHeight = offscreenImage.height;
|
||||
|
||||
// Now figure how much we have to scale the decoded image down
|
||||
// (or up) so that it covers the specified output dimensions.
|
||||
var widthScale = outputWidth / actualWidth;
|
||||
var heightScale = outputHeight / actualHeight;
|
||||
var scale = Math.max(widthScale, heightScale);
|
||||
|
||||
// Scaling the output dimensions by this much tells us the
|
||||
// dimensions of the crop area on the decoded image
|
||||
var cropWidth = Math.round(outputWidth / scale);
|
||||
var cropHeight = Math.round(outputHeight / scale);
|
||||
|
||||
// Now center that crop region within the decoded image
|
||||
var cropLeft = Math.floor((actualWidth - cropWidth) / 2);
|
||||
var cropTop = Math.floor((actualHeight - cropHeight) / 2);
|
||||
|
||||
// Set up the canvas we need to copy the crop region into
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = outputWidth;
|
||||
canvas.height = outputHeight;
|
||||
var context = canvas.getContext('2d', { willReadFrequently: true });
|
||||
|
||||
// Copy the image into the canvas
|
||||
context.drawImage(offscreenImage,
|
||||
cropLeft, cropTop, cropWidth, cropHeight,
|
||||
0, 0, outputWidth, outputHeight);
|
||||
|
||||
// We're done with the image now: try to release the decoded
|
||||
// image memory as fast as we can
|
||||
cleanupImage();
|
||||
|
||||
// Now encode the pixels in the canvas as an image blob
|
||||
canvas.toBlob(function(blob) {
|
||||
// We've got the encoded image in the blob, so we don't need
|
||||
// the pixels in the canvas anymore. Try to release that memory
|
||||
// right away, too.
|
||||
canvas.width = 0;
|
||||
resolve(blob);
|
||||
}, outputType, encoderOptions);
|
||||
};
|
||||
|
||||
function cleanupImage() {
|
||||
offscreenImage.onerror = offscreenImage.onload = '';
|
||||
offscreenImage.onload = offscreenImage.onerror = null; offscreenImage.removeAttribute('src');
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// This module defines a Downsample object with static
|
||||
// methods that return objects representing media fragments for
|
||||
// downsampling images while they are decoded. The current implementation
|
||||
// is based on the #-moz-samplesize media fragment. But because of
|
||||
// problems with that fragment (see bug 1004908) it seems likely that a
|
||||
// new syntax or new fragment will be introduced. If that happens, we can
|
||||
// just change this module and not have to change anything else that
|
||||
// depends on it.
|
||||
//
|
||||
// The method Downsample.areaAtLeast(scale) returns an object
|
||||
// representing a media fragment to use to decode an image downsampled by
|
||||
// at least as much as the specified scale. If you are trying to preview
|
||||
// an 8mp image and don't want to use more than 2mp of image memory, for
|
||||
// example, you would pass a scale of .25 (2mp/8mp) here, and the
|
||||
// resulting media fragment could be appended to the url to make the
|
||||
// image decode at a size equal to or smaller than 2mp.
|
||||
//
|
||||
// The method Downsample.sizeNoMoreThan(scale) returns a media fragment
|
||||
// object that you can use to reduce the dimensions of an image as much
|
||||
// as possible without exceeding the specified scale. If you have a
|
||||
// 1600x1200 image and want to decode it to produce an image that is as
|
||||
// small as possible but at least 160x120, you would pass a scale of 0.1.
|
||||
//
|
||||
// The returned objects have a dimensionScale property that specifies how
|
||||
// they affect the dimensions of the image and an areaScale property that
|
||||
// specifies how much they affect the area (number of pixels) in an
|
||||
// image. (The areaScale is just the square of the scale.) To avoid
|
||||
// floating-point rounding issues, the values of these scale properties
|
||||
// are rounded to the nearest hundredth.
|
||||
//
|
||||
// The returned objects also have a scale() method that scales a
|
||||
// dimension with proper rounding (it rounds up to the nearest integer
|
||||
// just as libjpeg does).
|
||||
//
|
||||
// Each object also has a toString() method that returns the required
|
||||
// media fragment (including the hash mark) so you can simply use
|
||||
// string concatentation to append one of these objects to the URL of
|
||||
// the image you want to decode.
|
||||
//
|
||||
// Downsample.NONE is a no-op media fragment object with scale set to
|
||||
// 1, and a toString() method that returns the empty string.
|
||||
//
|
||||
(function(exports) {
|
||||
'use strict'; // jshint ignore:line
|
||||
|
||||
// Round to the nearest hundredth to combat floating point rounding errors
|
||||
function round(x) {
|
||||
return Math.round(x * 100) / 100;
|
||||
}
|
||||
|
||||
//
|
||||
// A factory method for returning an object that represents a
|
||||
// #-moz-samplesize media fragment. The use of Math.ceil() in the
|
||||
// scale method is from jpeg_core_output_dimensions() in
|
||||
// media/libjpeg/jdmaster.c and jdiv_round_up() in media/libjpeg/jutils.c
|
||||
//
|
||||
function MozSampleSize(n, scale) {
|
||||
return Object.freeze({
|
||||
dimensionScale: round(scale),
|
||||
areaScale: round(scale * scale),
|
||||
toString: function() { return '#-moz-samplesize=' + n; },
|
||||
scale: function(x) { return Math.ceil(x * scale); }
|
||||
});
|
||||
}
|
||||
|
||||
// A fragment object that represents no downsampling with no fragment
|
||||
var NONE = Object.freeze({
|
||||
dimensionScale: 1,
|
||||
areaScale: 1,
|
||||
toString: function() { return ''; },
|
||||
scale: function(x) { return x; }
|
||||
});
|
||||
|
||||
//
|
||||
// The five possible #-moz-samplesize values.
|
||||
// The mapping from sample size to scale comes from:
|
||||
// the moz-samplesize code in /image/decoders/nsJPEGDecoder.cpp and
|
||||
// the jpeg_core_output_dimensions() function in media/libjpeg/jdmaster.c
|
||||
//
|
||||
var fragments = [
|
||||
NONE,
|
||||
// samplesize=2 reduces size by 1/2 and area by 1/4, etc.
|
||||
MozSampleSize(2, 1 / 2),
|
||||
MozSampleSize(3, 3 / 8),
|
||||
MozSampleSize(4, 1 / 4),
|
||||
MozSampleSize(8, 1 / 8)
|
||||
];
|
||||
|
||||
// Return the fragment object that has the largest scale and downsamples the
|
||||
// dimensions of an image at least as much as the specified scale.
|
||||
// If none of the choices scales enough, return the one that comes closest
|
||||
function sizeAtLeast(scale) {
|
||||
scale = round(scale);
|
||||
for (var i = 0; i < fragments.length; i++) {
|
||||
var f = fragments[i];
|
||||
if (f.dimensionScale <= scale) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
return fragments[fragments.length - 1];
|
||||
}
|
||||
|
||||
// Return the fragment object that downsamples an image as far as possible
|
||||
// without going beyond the specified scale. This might return NONE.
|
||||
function sizeNoMoreThan(scale) {
|
||||
scale = round(scale);
|
||||
for (var i = fragments.length - 1; i >= 0; i--) {
|
||||
var f = fragments[i];
|
||||
if (f.dimensionScale >= scale) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
return NONE;
|
||||
}
|
||||
|
||||
// Return the fragment object that has the largest scale and downsamples the
|
||||
// area of an image at least as much as the specified scale.
|
||||
// If none of the choices scales enough, return the one that comes closest
|
||||
function areaAtLeast(scale) {
|
||||
scale = round(scale);
|
||||
for (var i = 0; i < fragments.length; i++) {
|
||||
var f = fragments[i];
|
||||
if (f.areaScale <= scale) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
return fragments[fragments.length - 1];
|
||||
}
|
||||
|
||||
// Return the fragment object that downsamples the area of an image
|
||||
// as far as possible without going beyond the specified scale. This
|
||||
// might return NONE.
|
||||
function areaNoMoreThan(scale) {
|
||||
scale = round(scale);
|
||||
for (var i = fragments.length - 1; i >= 0; i--) {
|
||||
var f = fragments[i];
|
||||
if (f.areaScale >= scale) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
return NONE;
|
||||
}
|
||||
|
||||
exports.Downsample = {
|
||||
sizeAtLeast: sizeAtLeast,
|
||||
sizeNoMoreThan: sizeNoMoreThan,
|
||||
areaAtLeast: areaAtLeast,
|
||||
areaNoMoreThan: areaNoMoreThan,
|
||||
NONE: NONE,
|
||||
MAX_SIZE_REDUCTION: 1 / fragments[fragments.length - 1].dimensionScale,
|
||||
MAX_AREA_REDUCTION: 1 / fragments[fragments.length - 1].areaScale
|
||||
};
|
||||
}(exports.ImageUtils));
|
||||
})(window);
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
;'use strict';(function(exports){var InputApp=function(domApp,dynamicInputs){this.domApp=domApp;this.dynamicInputs=dynamicInputs||{};if(!this.domApp.manifest){throw new Error('InputApp: should not be constructed before DOMApplication '+'has manifest ready.');}};InputApp.prototype.getInputs=function(){var dict={};var inputId;for(inputId in this.domApp.manifest.inputs){dict[inputId]=this.domApp.manifest.inputs[inputId];delete dict[inputId].nameL10nId;}
|
||||
for(inputId in this.dynamicInputs){if(inputId in dict){break;}
|
||||
dict[inputId]=this.dynamicInputs[inputId];dict[inputId].isDynamic=true;}
|
||||
if(this.domApp.manifest.type==='certified'){if('emoji'in dict){dict.emoji.nameL10nId='keyboard-layout-emoji';}}
|
||||
return dict;};var InputAppListSettings=function(){this._values={enable3rdPartyApps:undefined,dynamicInputs:undefined};this._bindCallback=null;this.ready=false;this._promise=null;};InputAppListSettings.prototype.onchange=null;InputAppListSettings.prototype.SETTINGS_KEY_THIRD_PARTY_APP_ENABLED='keyboard.3rd-party-app.enabled';InputAppListSettings.prototype.SETTINGS_KEY_DYNAMIC_INPUTS='keyboard.dynamic-inputs';InputAppListSettings.prototype.start=function(){if(navigator.mozSettings){this._bindCallback=this.callback.bind(this);navigator.mozSettings.addObserver(this.SETTINGS_KEY_THIRD_PARTY_APP_ENABLED,this._bindCallback);navigator.mozSettings.addObserver(this.SETTINGS_KEY_DYNAMIC_INPUTS,this._bindCallback);}
|
||||
this.getSettings().then(function(){this.ready=true;}.bind(this));};InputAppListSettings.prototype.getSettings=function(){if(this.ready){return this.getSettingsSync();}
|
||||
return this.getSettingsAsync();};InputAppListSettings.prototype.getSettingsSync=function(){if(!this.ready){console.warn('getSettingsSync: getSettingsSync called before ready.');}
|
||||
return this._values;};InputAppListSettings.prototype.getSettingsAsync=function(){if(this._promise){return this._promise;}
|
||||
var lock=navigator.mozSettings.createLock();var p=this._promise=Promise.all([lock.get(this.SETTINGS_KEY_THIRD_PARTY_APP_ENABLED),lock.get(this.SETTINGS_KEY_DYNAMIC_INPUTS)]).then(function(values){this._values.enable3rdPartyApps=values[0][this.SETTINGS_KEY_THIRD_PARTY_APP_ENABLED];this._values.dynamicInputs=values[1][this.SETTINGS_KEY_DYNAMIC_INPUTS];return this._values;}.bind(this)).catch(function(e){console.error('InputAppListSettings: Fail to get setting.',e);this._promise=null;this._values.enable3rdPartyApps=false;this._values.dynamicInputs={};return this._values;}.bind(this));return p;};InputAppListSettings.prototype.callback=function(obj){switch(obj.settingName){case this.SETTINGS_KEY_THIRD_PARTY_APP_ENABLED:this._values.enable3rdPartyApps=obj.settingValue;break;case this.SETTINGS_KEY_DYNAMIC_INPUTS:this._values.dynamicInputs=obj.settingValue;break;}
|
||||
if(typeof this.onchange==='function'){this.onchange(this._values);}};InputAppListSettings.prototype.stop=function(){this._values={enable3rdPartyApps:undefined,dynamicInputs:undefined};this.ready=false;this._promise=null;if(navigator.mozSettings){navigator.mozSettings.removeObserver(this.SETTINGS_KEY_THIRD_PARTY_APP_ENABLED,this._bindCallback);navigator.mozSettings.removeObserver(this.SETTINGS_KEY_DYNAMIC_INPUTS,this._bindCallback);this._bindCallback=null;}};var InputAppList=function(){this.ready=false;this._inputApps=null;this.settings=null;this._getListPromise=null;};InputAppList.prototype.onready=null;InputAppList.prototype.onupdate=null;InputAppList.prototype.SETTINGS_KEY_THIRD_PARTY_APP_ENABLED='keyboard.3rd-party-app.enabled';InputAppList.prototype.start=function(){if(navigator.mozApps&&navigator.mozApps.mgmt){navigator.mozApps.mgmt.addEventListener('install',this);navigator.mozApps.mgmt.addEventListener('uninstall',this);}
|
||||
this.settings=new InputAppListSettings();this.settings.onchange=this._refresh.bind(this);this.settings.start();this.getList().then(function(){this.ready=true;if(typeof this.onready==='function'){return this.onready(this._inputApps);}}.bind(this)).catch(function(e){console.error(e);});};InputAppList.prototype.getList=function(){if(this._getListPromise){return this._getListPromise;}
|
||||
var p=this._getListPromise=Promise.all([this.settings.getSettings(),this._getAllApps()]).then(this._setInputApps.bind(this)).then(function(){return this._inputApps;}.bind(this)).catch(function(e){this._getListPromise=null;console.error(e);}.bind(this));return p;};InputAppList.prototype.getListSync=function(){if(!this.ready){console.warn('InputAppList: getListSync called when list is not ready.');}
|
||||
return this._inputApps;};InputAppList.prototype.handleEvent=function(evt){var updated=false;switch(evt.type){case'install':var app=evt.application;if(app.downloading){app.addEventListener('downloadsuccess',this);return;}
|
||||
updated=this._addInputApp(app);break;case'downloadsuccess':updated=this._addInputApp(evt.target);break;case'uninstall':updated=this._removeInputApp(evt.application);break;}
|
||||
if(updated&&typeof this.onupdate==='function'){this.onupdate(this._inputApps);}};InputAppList.prototype.stop=function(){this.ready=false;this._inputApps=null;this.settings=null;this._getListPromise=null;if(navigator.mozApps&&navigator.mozApps.mgmt){navigator.mozApps.mgmt.removeEventListener('install',this);navigator.mozApps.mgmt.removeEventListener('uninstall',this);}};InputAppList.prototype._getAllApps=function(){if(!navigator.mozApps||!navigator.mozApps.mgmt){console.error('InputAppList: mozApps.mgmt not available.');return[];}
|
||||
return Promise.resolve(navigator.mozApps.mgmt.getAll()).catch(function(e){console.error('InputAppList: Fail to get app list.',e);return[];});};InputAppList.prototype._setInputApps=function(values){var apps=values[1];var dynamicInputs=this.settings.getSettingsSync().dynamicInputs||{};var inputApps=apps.filter(this._isInputApp,this).map(function(domApp){var inputApp=new InputApp(domApp,dynamicInputs[domApp.manifestURL]);return inputApp;});if(inputApps.length===0){console.error('InputAppList: No input apps installed?');}
|
||||
this._inputApps=inputApps;};InputAppList.prototype._addInputApp=function(domApp){if(!this._isInputApp(domApp)){return false;}
|
||||
this._inputApps.push(new InputApp(domApp));return true;};InputAppList.prototype._removeInputApp=function(domApp){var index=this._inputApps.findIndex(function(appInList){return(appInList.domApp.manifestURL===domApp.manifestURL);});if(index===-1){return false;}
|
||||
this._inputApps.splice(index,1);return true;};InputAppList.prototype._isInputApp=function(domApp){if(!domApp.manifest||'input'!==domApp.manifest.role){return false;}
|
||||
if(domApp.manifest.type!=='certified'&&domApp.manifest.type!=='privileged'){return false;}
|
||||
var settingValues=this.settings.getSettingsSync();if(!settingValues.enable3rdPartyApps&&domApp.manifest.type!=='certified'){console.warn('InputAppList: '+'A 3rd-party input app is installed but the feature is not enabled.',domApp.manifestURL);return false;}
|
||||
if(domApp.manifest.permissions&&(typeof domApp.manifest.permissions.input!=='object')){return false;}
|
||||
if(typeof domApp.manifest.inputs!=='object'){return false;}
|
||||
return true;};InputAppList.prototype._refresh=function(){this._getListPromise=null;this.getList().then(function(){if(typeof this.onupdate==='function'){return this.onupdate(this._inputApps);}}.bind(this)).catch(function(e){console.error(e);});};exports.InputAppListSettings=InputAppListSettings;exports.InputAppList=InputAppList;exports.InputApp=InputApp;}(window));
|
||||
280
assets/webapps/ftu.gaiamobile.org/shared/js/intl/l20n.js
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
;var _createClass=(function(){function defineProperties(target,props){for(var i=0;i<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||false;descriptor.configurable=true;if('value'in descriptor)descriptor.writable=true;Object.defineProperty(target,descriptor.key,descriptor);}}return function(Constructor,protoProps,staticProps){if(protoProps)defineProperties(Constructor.prototype,protoProps);if(staticProps)defineProperties(Constructor,staticProps);return Constructor;};})();function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError('Cannot call a class as a function');}}
|
||||
(function(){'use strict';function emit(listeners,...args){const type=args.shift();if(listeners['*']){listeners['*'].slice().forEach(listener=>listener.apply(this,args));}
|
||||
if(listeners[type]){listeners[type].slice().forEach(listener=>listener.apply(this,args));}}
|
||||
function addEventListener(listeners,type,listener){if(!(type in listeners)){listeners[type]=[];}
|
||||
listeners[type].push(listener);}
|
||||
function removeEventListener(listeners,type,listener){const typeListeners=listeners[type];const pos=typeListeners.indexOf(listener);if(pos===-1){return;}
|
||||
typeListeners.splice(pos,1);}
|
||||
let Client=(function(){function Client(remote){_classCallCheck(this,Client);this.id=this;this.remote=remote;const listeners={};this.on=(...args)=>addEventListener(listeners,...args);this.emit=(...args)=>emit(listeners,...args);}
|
||||
_createClass(Client,[{key:'method',value:function method(name,...args){return this.remote[name](...args);}}]);return Client;})();function broadcast(type,data){Array.from(this.ctxs.keys()).forEach(client=>client.emit(type,data));}
|
||||
function L10nError(message,id,lang){this.name='L10nError';this.message=message;this.id=id;this.lang=lang;}
|
||||
L10nError.prototype=Object.create(Error.prototype);L10nError.prototype.constructor=L10nError;function load(type,url){return new Promise(function(resolve,reject){const xhr=new XMLHttpRequest();if(xhr.overrideMimeType){xhr.overrideMimeType(type);}
|
||||
xhr.open('GET',url,true);if(type==='application/json'){xhr.responseType='json';}
|
||||
xhr.addEventListener('load',function io_onload(e){if(e.target.status===200||e.target.status===0){resolve(e.target.response||e.target.responseText);}else{reject(new L10nError('Not found: '+url));}});xhr.addEventListener('error',reject);xhr.addEventListener('timeout',reject);try{xhr.send(null);}catch(e){if(e.name==='NS_ERROR_FILE_NOT_FOUND'){reject(new L10nError('Not found: '+url));}else{throw e;}}});}
|
||||
const io={extra:function(code,ver,path,type){return navigator.mozApps.getLocalizationResource(code,ver,path,type);},app:function(code,ver,path,type){switch(type){case'text':return load('text/plain',path);case'json':return load('application/json',path);default:throw new L10nError('Unknown file type: '+type);}}};function fetchResource(res,{code,src,ver}){const url=res.replace('{locale}',code);const type=res.endsWith('.json')?'json':'text';return io[src](code,ver,url,type);}
|
||||
const KNOWN_MACROS=['plural'];const MAX_PLACEABLE_LENGTH=2500;const FSI='';const PDI='';const resolutionChain=new WeakSet();function format(ctx,lang,args,entity){if(typeof entity==='string'){return[{},entity];}
|
||||
if(resolutionChain.has(entity)){throw new L10nError('Cyclic reference detected');}
|
||||
resolutionChain.add(entity);let rv;try{rv=resolveValue({},ctx,lang,args,entity.value,entity.index);}finally{resolutionChain.delete(entity);}
|
||||
return rv;}
|
||||
function resolveIdentifier(ctx,lang,args,id){if(KNOWN_MACROS.indexOf(id)>-1){return[{},ctx._getMacro(lang,id)];}
|
||||
if(args&&args.hasOwnProperty(id)){if(typeof args[id]==='string'||typeof args[id]==='number'&&!isNaN(args[id])){return[{},args[id]];}else{throw new L10nError('Arg must be a string or a number: '+id);}}
|
||||
if(id==='__proto__'){throw new L10nError('Illegal id: '+id);}
|
||||
const entity=ctx._getEntity(lang,id);if(entity){return format(ctx,lang,args,entity);}
|
||||
throw new L10nError('Unknown reference: '+id);}
|
||||
function subPlaceable(locals,ctx,lang,args,id){let newLocals,value;try{[newLocals,value]=resolveIdentifier(ctx,lang,args,id);}catch(err){return[{error:err},FSI+'{{ '+id+' }}'+PDI];}
|
||||
if(typeof value==='number'){const formatter=ctx._getNumberFormatter(lang);return[newLocals,formatter.format(value)];}
|
||||
if(typeof value==='string'){if(value.length>=MAX_PLACEABLE_LENGTH){throw new L10nError('Too many characters in placeable ('+value.length+', max allowed is '+MAX_PLACEABLE_LENGTH+')');}
|
||||
return[newLocals,FSI+value+PDI];}
|
||||
return[{},FSI+'{{ '+id+' }}'+PDI];}
|
||||
function interpolate(locals,ctx,lang,args,arr){return arr.reduce(function([localsSeq,valueSeq],cur){if(typeof cur==='string'){return[localsSeq,valueSeq+cur];}else{const[,value]=subPlaceable(locals,ctx,lang,args,cur.name);return[localsSeq,valueSeq+value];}},[locals,'']);}
|
||||
function resolveSelector(ctx,lang,args,expr,index){let selectorName;if(index[0].type==='call'&&index[0].expr.type==='prop'&&index[0].expr.expr.name==='cldr'){selectorName='plural';}else{selectorName=index[0].name;}
|
||||
const selector=resolveIdentifier(ctx,lang,args,selectorName)[1];if(typeof selector!=='function'){return selector;}
|
||||
const argValue=index[0].args?resolveIdentifier(ctx,lang,args,index[0].args[0].name)[1]:undefined;if(selectorName==='plural'){if(argValue===0&&'zero'in expr){return'zero';}
|
||||
if(argValue===1&&'one'in expr){return'one';}
|
||||
if(argValue===2&&'two'in expr){return'two';}}
|
||||
return selector(argValue);}
|
||||
function resolveValue(locals,ctx,lang,args,expr,index){if(!expr){return[locals,expr];}
|
||||
if(typeof expr==='string'||typeof expr==='boolean'||typeof expr==='number'){return[locals,expr];}
|
||||
if(Array.isArray(expr)){return interpolate(locals,ctx,lang,args,expr);}
|
||||
if(index){const selector=resolveSelector(ctx,lang,args,expr,index);if(selector in expr){return resolveValue(locals,ctx,lang,args,expr[selector]);}}
|
||||
const defaultKey=expr.__default||'other';if(defaultKey in expr){return resolveValue(locals,ctx,lang,args,expr[defaultKey]);}
|
||||
throw new L10nError('Unresolvable value');}
|
||||
const locales2rules={'af':3,'ak':4,'am':4,'ar':1,'asa':3,'az':0,'be':11,'bem':3,'bez':3,'bg':3,'bh':4,'bm':0,'bn':3,'bo':0,'br':20,'brx':3,'bs':11,'ca':3,'cgg':3,'chr':3,'cs':12,'cy':17,'da':3,'de':3,'dv':3,'dz':0,'ee':3,'el':3,'en':3,'eo':3,'es':3,'et':3,'eu':3,'fa':0,'ff':5,'fi':3,'fil':4,'fo':3,'fr':5,'fur':3,'fy':3,'ga':8,'gd':24,'gl':3,'gsw':3,'gu':3,'guw':4,'gv':23,'ha':3,'haw':3,'he':2,'hi':4,'hr':11,'hu':0,'id':0,'ig':0,'ii':0,'is':3,'it':3,'iu':7,'ja':0,'jmc':3,'jv':0,'ka':0,'kab':5,'kaj':3,'kcg':3,'kde':0,'kea':0,'kk':3,'kl':3,'km':0,'kn':0,'ko':0,'ksb':3,'ksh':21,'ku':3,'kw':7,'lag':18,'lb':3,'lg':3,'ln':4,'lo':0,'lt':10,'lv':6,'mas':3,'mg':4,'mk':16,'ml':3,'mn':3,'mo':9,'mr':3,'ms':0,'mt':15,'my':0,'nah':3,'naq':7,'nb':3,'nd':3,'ne':3,'nl':3,'nn':3,'no':3,'nr':3,'nso':4,'ny':3,'nyn':3,'om':3,'or':3,'pa':3,'pap':3,'pl':13,'ps':3,'pt':3,'rm':3,'ro':9,'rof':3,'ru':11,'rwk':3,'sah':0,'saq':3,'se':7,'seh':3,'ses':0,'sg':0,'sh':11,'shi':19,'sk':12,'sl':14,'sma':7,'smi':7,'smj':7,'smn':7,'sms':7,'sn':3,'so':3,'sq':3,'sr':11,'ss':3,'ssy':3,'st':3,'sv':3,'sw':3,'syr':3,'ta':3,'te':3,'teo':3,'th':0,'ti':4,'tig':3,'tk':3,'tl':4,'tn':3,'to':0,'tr':0,'ts':3,'tzm':22,'uk':11,'ur':3,'ve':3,'vi':0,'vun':3,'wa':4,'wae':3,'wo':0,'xh':3,'xog':3,'yo':0,'zh':0,'zu':3};function isIn(n,list){return list.indexOf(n)!==-1;}
|
||||
function isBetween(n,start,end){return typeof n===typeof start&&start<=n&&n<=end;}
|
||||
const pluralRules={'0':function(){return'other';},'1':function(n){if(isBetween(n%100,3,10)){return'few';}
|
||||
if(n===0){return'zero';}
|
||||
if(isBetween(n%100,11,99)){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'2':function(n){if(n!==0&&n%10===0){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'3':function(n){if(n===1){return'one';}
|
||||
return'other';},'4':function(n){if(isBetween(n,0,1)){return'one';}
|
||||
return'other';},'5':function(n){if(isBetween(n,0,2)&&n!==2){return'one';}
|
||||
return'other';},'6':function(n){if(n===0){return'zero';}
|
||||
if(n%10===1&&n%100!==11){return'one';}
|
||||
return'other';},'7':function(n){if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'8':function(n){if(isBetween(n,3,6)){return'few';}
|
||||
if(isBetween(n,7,10)){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'9':function(n){if(n===0||n!==1&&isBetween(n%100,1,19)){return'few';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'10':function(n){if(isBetween(n%10,2,9)&&!isBetween(n%100,11,19)){return'few';}
|
||||
if(n%10===1&&!isBetween(n%100,11,19)){return'one';}
|
||||
return'other';},'11':function(n){if(isBetween(n%10,2,4)&&!isBetween(n%100,12,14)){return'few';}
|
||||
if(n%10===0||isBetween(n%10,5,9)||isBetween(n%100,11,14)){return'many';}
|
||||
if(n%10===1&&n%100!==11){return'one';}
|
||||
return'other';},'12':function(n){if(isBetween(n,2,4)){return'few';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'13':function(n){if(isBetween(n%10,2,4)&&!isBetween(n%100,12,14)){return'few';}
|
||||
if(n!==1&&isBetween(n%10,0,1)||isBetween(n%10,5,9)||isBetween(n%100,12,14)){return'many';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'14':function(n){if(isBetween(n%100,3,4)){return'few';}
|
||||
if(n%100===2){return'two';}
|
||||
if(n%100===1){return'one';}
|
||||
return'other';},'15':function(n){if(n===0||isBetween(n%100,2,10)){return'few';}
|
||||
if(isBetween(n%100,11,19)){return'many';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'16':function(n){if(n%10===1&&n!==11){return'one';}
|
||||
return'other';},'17':function(n){if(n===3){return'few';}
|
||||
if(n===0){return'zero';}
|
||||
if(n===6){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'18':function(n){if(n===0){return'zero';}
|
||||
if(isBetween(n,0,2)&&n!==0&&n!==2){return'one';}
|
||||
return'other';},'19':function(n){if(isBetween(n,2,10)){return'few';}
|
||||
if(isBetween(n,0,1)){return'one';}
|
||||
return'other';},'20':function(n){if((isBetween(n%10,3,4)||n%10===9)&&!(isBetween(n%100,10,19)||isBetween(n%100,70,79)||isBetween(n%100,90,99))){return'few';}
|
||||
if(n%1000000===0&&n!==0){return'many';}
|
||||
if(n%10===2&&!isIn(n%100,[12,72,92])){return'two';}
|
||||
if(n%10===1&&!isIn(n%100,[11,71,91])){return'one';}
|
||||
return'other';},'21':function(n){if(n===0){return'zero';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'22':function(n){if(isBetween(n,0,1)||isBetween(n,11,99)){return'one';}
|
||||
return'other';},'23':function(n){if(isBetween(n%10,1,2)||n%20===0){return'one';}
|
||||
return'other';},'24':function(n){if(isBetween(n,3,10)||isBetween(n,13,19)){return'few';}
|
||||
if(isIn(n,[2,12])){return'two';}
|
||||
if(isIn(n,[1,11])){return'one';}
|
||||
return'other';}};function getPluralRule(code){const index=locales2rules[code.replace(/-.*$/,'')];if(!(index in pluralRules)){return function(){return'other';};}
|
||||
return pluralRules[index];}
|
||||
const L20nIntl=typeof Intl!=='undefined'?Intl:{NumberFormat:function(){return{format:function(v){return v;}};}};let Context=(function(){function Context(env,langs,resIds){_classCallCheck(this,Context);this.langs=langs;this.resIds=resIds;this.env=env;this.emit=(type,evt)=>env.emit(type,evt,this);}
|
||||
_createClass(Context,[{key:'_formatTuple',value:function _formatTuple(lang,args,entity,id,key){try{return format(this,lang,args,entity);}catch(err){err.id=key?id+'::'+key:id;err.lang=lang;this.emit('resolveerror',err);return[{error:err},err.id];}}},{key:'_formatEntity',value:function _formatEntity(lang,args,entity,id){const[,value]=this._formatTuple(lang,args,entity,id);const formatted={value,attrs:null};if(entity.attrs){formatted.attrs=Object.create(null);for(let key in entity.attrs){const[,attrValue]=this._formatTuple(lang,args,entity.attrs[key],id,key);formatted.attrs[key]=attrValue;}}
|
||||
return formatted;}},{key:'_formatValue',value:function _formatValue(lang,args,entity,id){return this._formatTuple(lang,args,entity,id)[1];}},{key:'fetch',value:function fetch(langs=this.langs){if(langs.length===0){return Promise.resolve(langs);}
|
||||
return Promise.all(this.resIds.map(resId=>this.env._getResource(langs[0],resId))).then(()=>langs);}},{key:'_resolve',value:function _resolve(langs,keys,formatter,prevResolved){const lang=langs[0];if(!lang){return reportMissing.call(this,keys,formatter,prevResolved);}
|
||||
let hasUnresolved=false;const resolved=keys.map((key,i)=>{if(prevResolved&&prevResolved[i]!==undefined){return prevResolved[i];}
|
||||
const[id,args]=Array.isArray(key)?key:[key,undefined];const entity=this._getEntity(lang,id);if(entity){return formatter.call(this,lang,args,entity,id);}
|
||||
this.emit('notfounderror',new L10nError('"'+id+'"'+' not found in '+lang.code,id,lang));hasUnresolved=true;});if(!hasUnresolved){return resolved;}
|
||||
return this.fetch(langs.slice(1)).then(nextLangs=>this._resolve(nextLangs,keys,formatter,resolved));}},{key:'formatEntities',value:function formatEntities(...keys){return this.fetch().then(langs=>this._resolve(langs,keys,this._formatEntity));}},{key:'formatValues',value:function formatValues(...keys){return this.fetch().then(langs=>this._resolve(langs,keys,this._formatValue));}},{key:'_getEntity',value:function _getEntity(lang,id){const cache=this.env.resCache;for(let i=0,resId;resId=this.resIds[i];i++){const resource=cache.get(resId+lang.code+lang.src);if(resource instanceof L10nError){continue;}
|
||||
if(id in resource){return resource[id];}}
|
||||
return undefined;}},{key:'_getNumberFormatter',value:function _getNumberFormatter(lang){if(!this.env.numberFormatters){this.env.numberFormatters=new Map();}
|
||||
if(!this.env.numberFormatters.has(lang)){const formatter=L20nIntl.NumberFormat(lang);this.env.numberFormatters.set(lang,formatter);return formatter;}
|
||||
return this.env.numberFormatters.get(lang);}},{key:'_getMacro',value:function _getMacro(lang,id){switch(id){case'plural':return getPluralRule(lang.code);default:return undefined;}}}]);return Context;})();function reportMissing(keys,formatter,resolved){const missingIds=new Set();keys.forEach((key,i)=>{if(resolved&&resolved[i]!==undefined){return;}
|
||||
const id=Array.isArray(key)?key[0]:key;missingIds.add(id);resolved[i]=formatter===this._formatValue?id:{value:id,attrs:null};});this.emit('notfounderror',new L10nError('"'+Array.from(missingIds).join(', ')+'"'+' not found in any language',missingIds));return resolved;}
|
||||
var MAX_PLACEABLES=100;var PropertiesParser={patterns:null,entryIds:null,emit:null,init:function(){this.patterns={comment:/^\s*#|^\s*$/,entity:/^([^=\s]+)\s*=\s*(.*)$/,multiline:/[^\\]\\$/,index:/\{\[\s*(\w+)(?:\(([^\)]*)\))?\s*\]\}/i,unicode:/\\u([0-9a-fA-F]{1,4})/g,entries:/[^\r\n]+/g,controlChars:/\\([\\\n\r\t\b\f\{\}\"\'])/g,placeables:/\{\{\s*([^\s]*?)\s*\}\}/};},parse:function(emit,source){if(!this.patterns){this.init();}
|
||||
this.emit=emit;var entries={};var lines=source.match(this.patterns.entries);if(!lines){return entries;}
|
||||
for(var i=0;i<lines.length;i++){var line=lines[i];if(this.patterns.comment.test(line)){continue;}
|
||||
while(this.patterns.multiline.test(line)&&i<lines.length){line=line.slice(0,-1)+lines[++i].trim();}
|
||||
var entityMatch=line.match(this.patterns.entity);if(entityMatch){try{this.parseEntity(entityMatch[1],entityMatch[2],entries);}catch(e){if(!this.emit){throw e;}}}}
|
||||
return entries;},parseEntity:function(id,value,entries){var name,key;var pos=id.indexOf('[');if(pos!==-1){name=id.substr(0,pos);key=id.substring(pos+1,id.length-1);}else{name=id;key=null;}
|
||||
var nameElements=name.split('.');if(nameElements.length>2){throw this.error('Error in ID: "'+name+'".'+' Nested attributes are not supported.');}
|
||||
var attr;if(nameElements.length>1){name=nameElements[0];attr=nameElements[1];if(attr[0]==='$'){throw this.error('Attribute can\'t start with "$"');}}else{attr=null;}
|
||||
this.setEntityValue(name,attr,key,this.unescapeString(value),entries);},setEntityValue:function(id,attr,key,rawValue,entries){var value=rawValue.indexOf('{{')>-1?this.parseString(rawValue):rawValue;var isSimpleValue=typeof value==='string';var root=entries;var isSimpleNode=typeof entries[id]==='string';if(!entries[id]&&(attr||key||!isSimpleValue)){entries[id]=Object.create(null);isSimpleNode=false;}
|
||||
if(attr){if(isSimpleNode){const val=entries[id];entries[id]=Object.create(null);entries[id].value=val;}
|
||||
if(!entries[id].attrs){entries[id].attrs=Object.create(null);}
|
||||
if(!entries[id].attrs&&!isSimpleValue){entries[id].attrs[attr]=Object.create(null);}
|
||||
root=entries[id].attrs;id=attr;}
|
||||
if(key){isSimpleNode=false;if(typeof root[id]==='string'){const val=root[id];root[id]=Object.create(null);root[id].index=this.parseIndex(val);root[id].value=Object.create(null);}
|
||||
root=root[id].value;id=key;isSimpleValue=true;}
|
||||
if(isSimpleValue){if(id in root){throw this.error('Duplicated id: '+id);}
|
||||
root[id]=value;}else{if(!root[id]){root[id]=Object.create(null);}
|
||||
root[id].value=value;}},parseString:function(str){var chunks=str.split(this.patterns.placeables);var complexStr=[];var len=chunks.length;var placeablesCount=(len-1)/2;if(placeablesCount>=MAX_PLACEABLES){throw this.error('Too many placeables ('+placeablesCount+', max allowed is '+MAX_PLACEABLES+')');}
|
||||
for(var i=0;i<chunks.length;i++){if(chunks[i].length===0){continue;}
|
||||
if(i%2===1){complexStr.push({type:'idOrVar',name:chunks[i]});}else{complexStr.push(chunks[i]);}}
|
||||
return complexStr;},unescapeString:function(str){if(str.lastIndexOf('\\')!==-1){str=str.replace(this.patterns.controlChars,'$1');}
|
||||
return str.replace(this.patterns.unicode,function(match,token){return String.fromCodePoint(parseInt(token,16));});},parseIndex:function(str){var match=str.match(this.patterns.index);if(!match){throw new L10nError('Malformed index');}
|
||||
if(match[2]){return[{type:'call',expr:{type:'prop',expr:{type:'glob',name:'cldr'},prop:'plural',cmpt:false},args:[{type:'idOrVar',name:match[2]}]}];}else{return[{type:'idOrVar',name:match[1]}];}},error:function(msg,type='parsererror'){const err=new L10nError(msg);if(this.emit){this.emit(type,err);}
|
||||
return err;}};const MAX_PLACEABLES$1=100;var L20nParser={parse:function(emit,string){this._source=string;this._index=0;this._length=string.length;this.entries=Object.create(null);this.emit=emit;return this.getResource();},getResource:function(){this.getWS();while(this._index<this._length){try{this.getEntry();}catch(e){if(e instanceof L10nError){this.getJunkEntry();if(!this.emit){throw e;}}else{throw e;}}
|
||||
if(this._index<this._length){this.getWS();}}
|
||||
return this.entries;},getEntry:function(){if(this._source[this._index]==='<'){++this._index;const id=this.getIdentifier();if(this._source[this._index]==='['){++this._index;return this.getEntity(id,this.getItemList(this.getExpression,']'));}
|
||||
return this.getEntity(id);}
|
||||
if(this._source.startsWith('/*',this._index)){return this.getComment();}
|
||||
throw this.error('Invalid entry');},getEntity:function(id,index){if(!this.getRequiredWS()){throw this.error('Expected white space');}
|
||||
const ch=this._source[this._index];const hasIndex=index!==undefined;const value=this.getValue(ch,hasIndex,hasIndex);let attrs;if(value===undefined){if(ch==='>'){throw this.error('Expected ">"');}
|
||||
attrs=this.getAttributes();}else{const ws1=this.getRequiredWS();if(this._source[this._index]!=='>'){if(!ws1){throw this.error('Expected ">"');}
|
||||
attrs=this.getAttributes();}}
|
||||
++this._index;if(id in this.entries){throw this.error('Duplicate entry ID "'+id,'duplicateerror');}
|
||||
if(!attrs&&!index&&typeof value==='string'){this.entries[id]=value;}else{this.entries[id]={value,attrs,index};}},getValue:function(ch=this._source[this._index],index=false,required=true){switch(ch){case'\'':case'"':return this.getString(ch,1);case'{':return this.getHash(index);}
|
||||
if(required){throw this.error('Unknown value type');}
|
||||
return;},getWS:function(){let cc=this._source.charCodeAt(this._index);while(cc===32||cc===10||cc===9||cc===13){cc=this._source.charCodeAt(++this._index);}},getRequiredWS:function(){const pos=this._index;let cc=this._source.charCodeAt(pos);while(cc===32||cc===10||cc===9||cc===13){cc=this._source.charCodeAt(++this._index);}
|
||||
return this._index!==pos;},getIdentifier:function(){const start=this._index;let cc=this._source.charCodeAt(this._index);if(cc>=97&&cc<=122||cc>=65&&cc<=90||cc===95){cc=this._source.charCodeAt(++this._index);}else{throw this.error('Identifier has to start with [a-zA-Z_]');}
|
||||
while(cc>=97&&cc<=122||cc>=65&&cc<=90||cc>=48&&cc<=57||cc===95){cc=this._source.charCodeAt(++this._index);}
|
||||
return this._source.slice(start,this._index);},getUnicodeChar:function(){for(let i=0;i<4;i++){let cc=this._source.charCodeAt(++this._index);if(cc>96&&cc<103||cc>64&&cc<71||cc>47&&cc<58){continue;}
|
||||
throw this.error('Illegal unicode escape sequence');}
|
||||
this._index++;return String.fromCharCode(parseInt(this._source.slice(this._index-4,this._index),16));},stringRe:/"|'|{{|\\/g,getString:function(opchar,opcharLen){const body=[];let placeables=0;this._index+=opcharLen;const start=this._index;let bufStart=start;let buf='';while(true){this.stringRe.lastIndex=this._index;const match=this.stringRe.exec(this._source);if(!match){throw this.error('Unclosed string literal');}
|
||||
if(match[0]==='"'||match[0]==='\''){if(match[0]!==opchar){this._index+=opcharLen;continue;}
|
||||
this._index=match.index+opcharLen;break;}
|
||||
if(match[0]==='{{'){if(placeables>MAX_PLACEABLES$1-1){throw this.error('Too many placeables, maximum allowed is '+MAX_PLACEABLES$1);}
|
||||
placeables++;if(match.index>bufStart||buf.length>0){body.push(buf+this._source.slice(bufStart,match.index));buf='';}
|
||||
this._index=match.index+2;this.getWS();body.push(this.getExpression());this.getWS();this._index+=2;bufStart=this._index;continue;}
|
||||
if(match[0]==='\\'){this._index=match.index+1;const ch2=this._source[this._index];if(ch2==='u'){buf+=this._source.slice(bufStart,match.index)+this.getUnicodeChar();}else if(ch2===opchar||ch2==='\\'){buf+=this._source.slice(bufStart,match.index)+ch2;this._index++;}else if(this._source.startsWith('{{',this._index)){buf+=this._source.slice(bufStart,match.index)+'{{';this._index+=2;}else{throw this.error('Illegal escape sequence');}
|
||||
bufStart=this._index;}}
|
||||
if(body.length===0){return buf+this._source.slice(bufStart,this._index-opcharLen);}
|
||||
if(this._index-opcharLen>bufStart||buf.length>0){body.push(buf+this._source.slice(bufStart,this._index-opcharLen));}
|
||||
return body;},getAttributes:function(){const attrs=Object.create(null);while(true){this.getAttribute(attrs);const ws1=this.getRequiredWS();const ch=this._source.charAt(this._index);if(ch==='>'){break;}else if(!ws1){throw this.error('Expected ">"');}}
|
||||
return attrs;},getAttribute:function(attrs){const key=this.getIdentifier();let index;if(this._source[this._index]==='['){++this._index;this.getWS();index=this.getItemList(this.getExpression,']');}
|
||||
this.getWS();if(this._source[this._index]!==':'){throw this.error('Expected ":"');}
|
||||
++this._index;this.getWS();const hasIndex=index!==undefined;const value=this.getValue(undefined,hasIndex);if(key in attrs){throw this.error('Duplicate attribute "'+key,'duplicateerror');}
|
||||
if(!index&&typeof value==='string'){attrs[key]=value;}else{attrs[key]={value,index};}},getHash:function(index){const items=Object.create(null);++this._index;this.getWS();let defKey;while(true){const[key,value,def]=this.getHashItem();items[key]=value;if(def){if(defKey){throw this.error('Default item redefinition forbidden');}
|
||||
defKey=key;}
|
||||
this.getWS();const comma=this._source[this._index]===',';if(comma){++this._index;this.getWS();}
|
||||
if(this._source[this._index]==='}'){++this._index;break;}
|
||||
if(!comma){throw this.error('Expected "}"');}}
|
||||
if(defKey){items.__default=defKey;}else if(!index){throw this.error('Unresolvable Hash Value');}
|
||||
return items;},getHashItem:function(){let defItem=false;if(this._source[this._index]==='*'){++this._index;defItem=true;}
|
||||
const key=this.getIdentifier();this.getWS();if(this._source[this._index]!==':'){throw this.error('Expected ":"');}
|
||||
++this._index;this.getWS();return[key,this.getValue(),defItem];},getComment:function(){this._index+=2;const start=this._index;const end=this._source.indexOf('*/',start);if(end===-1){throw this.error('Comment without a closing tag');}
|
||||
this._index=end+2;},getExpression:function(){let exp=this.getPrimaryExpression();while(true){let ch=this._source[this._index];if(ch==='.'||ch==='['){++this._index;exp=this.getPropertyExpression(exp,ch==='[');}else if(ch==='('){++this._index;exp=this.getCallExpression(exp);}else{break;}}
|
||||
return exp;},getPropertyExpression:function(idref,computed){let exp;if(computed){this.getWS();exp=this.getExpression();this.getWS();if(this._source[this._index]!==']'){throw this.error('Expected "]"');}
|
||||
++this._index;}else{exp=this.getIdentifier();}
|
||||
return{type:'prop',expr:idref,prop:exp,cmpt:computed};},getCallExpression:function(callee){this.getWS();return{type:'call',expr:callee,args:this.getItemList(this.getExpression,')')};},getPrimaryExpression:function(){const ch=this._source[this._index];switch(ch){case'$':++this._index;return{type:'var',name:this.getIdentifier()};case'@':++this._index;return{type:'glob',name:this.getIdentifier()};default:return{type:'id',name:this.getIdentifier()};}},getItemList:function(callback,closeChar){const items=[];let closed=false;this.getWS();if(this._source[this._index]===closeChar){++this._index;closed=true;}
|
||||
while(!closed){items.push(callback.call(this));this.getWS();let ch=this._source.charAt(this._index);switch(ch){case',':++this._index;this.getWS();break;case closeChar:++this._index;closed=true;break;default:throw this.error('Expected "," or "'+closeChar+'"');}}
|
||||
return items;},getJunkEntry:function(){const pos=this._index;let nextEntity=this._source.indexOf('<',pos);let nextComment=this._source.indexOf('/*',pos);if(nextEntity===-1){nextEntity=this._length;}
|
||||
if(nextComment===-1){nextComment=this._length;}
|
||||
let nextEntry=Math.min(nextEntity,nextComment);this._index=nextEntry;},error:function(message,type='parsererror'){const pos=this._index;let start=this._source.lastIndexOf('<',pos-1);const lastClose=this._source.lastIndexOf('>',pos-1);start=lastClose>start?lastClose+1:start;const context=this._source.slice(start,pos+10);const msg=message+' at pos '+pos+': `'+context+'`';const err=new L10nError(msg);if(this.emit){this.emit(type,err);}
|
||||
return err;}};function walkEntry(entry,fn){if(typeof entry==='string'){return fn(entry);}
|
||||
const newEntry=Object.create(null);if(entry.value){newEntry.value=walkValue(entry.value,fn);}
|
||||
if(entry.index){newEntry.index=entry.index;}
|
||||
if(entry.attrs){newEntry.attrs=Object.create(null);for(let key in entry.attrs){newEntry.attrs[key]=walkEntry(entry.attrs[key],fn);}}
|
||||
return newEntry;}
|
||||
function walkValue(value,fn){if(typeof value==='string'){return fn(value);}
|
||||
if(value.type){return value;}
|
||||
const newValue=Array.isArray(value)?[]:Object.create(null);const keys=Object.keys(value);for(let i=0,key;key=keys[i];i++){newValue[key]=walkValue(value[key],fn);}
|
||||
return newValue;}
|
||||
function createGetter(id,name){let _pseudo=null;return function getPseudo(){if(_pseudo){return _pseudo;}
|
||||
const reAlphas=/[a-zA-Z]/g;const reVowels=/[aeiouAEIOU]/g;const reWords=/[^\W0-9_]+/g;const reExcluded=/(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/;const charMaps={'fr-x-psaccent':'ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ[\\]^_`ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ','ar-x-psbidi':'∀ԐↃpƎɟפHIſӼ˥WNOԀÒᴚS⊥∩ɅMXʎZ[\\]ᵥ_,ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz'};const mods={'fr-x-psaccent':val=>val.replace(reVowels,match=>match+match.toLowerCase()),'ar-x-psbidi':val=>val.replace(reWords,match=>''+match+'')};const replaceChars=(map,val)=>val.replace(reAlphas,match=>map.charAt(match.charCodeAt(0)-65));const transform=val=>replaceChars(charMaps[id],mods[id](val));const apply=(fn,val)=>{if(!val){return val;}
|
||||
const parts=val.split(reExcluded);const modified=parts.map(function(part){if(reExcluded.test(part)){return part;}
|
||||
return fn(part);});return modified.join('');};return _pseudo={name:transform(name),process:str=>apply(transform,str)};};}
|
||||
const pseudo=Object.defineProperties(Object.create(null),{'fr-x-psaccent':{enumerable:true,get:createGetter('fr-x-psaccent','Runtime Accented')},'ar-x-psbidi':{enumerable:true,get:createGetter('ar-x-psbidi','Runtime Bidi')}});let Env=(function(){function Env(fetchResource){_classCallCheck(this,Env);this.fetchResource=fetchResource;this.resCache=new Map();this.resRefs=new Map();this.numberFormatters=null;this.parsers={properties:PropertiesParser,l20n:L20nParser};const listeners={};this.emit=emit.bind(this,listeners);this.addEventListener=addEventListener.bind(this,listeners);this.removeEventListener=removeEventListener.bind(this,listeners);}
|
||||
_createClass(Env,[{key:'createContext',value:function createContext(langs,resIds){const ctx=new Context(this,langs,resIds);resIds.forEach(resId=>{const usedBy=this.resRefs.get(resId)||0;this.resRefs.set(resId,usedBy+1);});return ctx;}},{key:'destroyContext',value:function destroyContext(ctx){ctx.resIds.forEach(resId=>{const usedBy=this.resRefs.get(resId)||0;if(usedBy>1){return this.resRefs.set(resId,usedBy-1);}
|
||||
this.resRefs.delete(resId);this.resCache.forEach((val,key)=>key.startsWith(resId)?this.resCache.delete(key):null);});}},{key:'_parse',value:function _parse(syntax,lang,data){const parser=this.parsers[syntax];if(!parser){return data;}
|
||||
const emit=(type,err)=>this.emit(type,amendError(lang,err));return parser.parse.call(parser,emit,data);}},{key:'_create',value:function _create(lang,entries){if(lang.src!=='pseudo'){return entries;}
|
||||
const pseudoentries=Object.create(null);for(let key in entries){pseudoentries[key]=walkEntry(entries[key],pseudo[lang.code].process);}
|
||||
return pseudoentries;}},{key:'_getResource',value:function _getResource(lang,res){const cache=this.resCache;const id=res+lang.code+lang.src;if(cache.has(id)){return cache.get(id);}
|
||||
const syntax=res.substr(res.lastIndexOf('.')+1);const saveEntries=data=>{const entries=this._parse(syntax,lang,data);cache.set(id,this._create(lang,entries));};const recover=err=>{err.lang=lang;this.emit('fetcherror',err);cache.set(id,err);};const langToFetch=lang.src==='pseudo'?{code:'en-US',src:'app',ver:lang.ver}:lang;const resource=this.fetchResource(res,langToFetch).then(saveEntries,recover);cache.set(id,resource);return resource;}}]);return Env;})();function amendError(lang,err){err.lang=lang;return err;}
|
||||
function prioritizeLocales(def,availableLangs,requested){let supportedLocale;for(let i=0;i<requested.length;i++){const locale=requested[i];if(availableLangs.indexOf(locale)!==-1){supportedLocale=locale;break;}}
|
||||
if(!supportedLocale||supportedLocale===def){return[def];}
|
||||
return[supportedLocale,def];}
|
||||
function negotiateLanguages({appVersion,defaultLang,availableLangs},additionalLangs,prevLangs,requestedLangs){const allAvailableLangs=Object.keys(availableLangs).concat(Object.keys(additionalLangs)).concat(Object.keys(pseudo));const newLangs=prioritizeLocales(defaultLang,allAvailableLangs,requestedLangs);const langs=newLangs.map(code=>({code:code,src:getLangSource(appVersion,availableLangs,additionalLangs,code),ver:appVersion}));return{langs,haveChanged:!arrEqual(prevLangs,newLangs)};}
|
||||
function arrEqual(arr1,arr2){return arr1.length===arr2.length&&arr1.every((elem,i)=>elem===arr2[i]);}
|
||||
function getMatchingLangpack(appVersion,langpacks){for(let i=0,langpack;langpack=langpacks[i];i++){if(langpack.target===appVersion){return langpack;}}
|
||||
return null;}
|
||||
function getLangSource(appVersion,availableLangs,additionalLangs,code){if(additionalLangs&&additionalLangs[code]){const lp=getMatchingLangpack(appVersion,additionalLangs[code]);if(lp&&(!(code in availableLangs)||parseInt(lp.revision)>availableLangs[code])){return'extra';}}
|
||||
if(code in pseudo&&!(code in availableLangs)){return'pseudo';}
|
||||
return'app';}
|
||||
let Remote=(function(){function Remote(fetchResource,broadcast){_classCallCheck(this,Remote);this.broadcast=broadcast;this.env=new Env(fetchResource);this.ctxs=new Map();}
|
||||
_createClass(Remote,[{key:'registerView',value:function registerView(view,resources,meta,additionalLangs,requestedLangs){const{langs}=negotiateLanguages(meta,additionalLangs,[],requestedLangs);this.ctxs.set(view,this.env.createContext(langs,resources));return langs;}},{key:'unregisterView',value:function unregisterView(view){this.ctxs.delete(view);return true;}},{key:'formatEntities',value:function formatEntities(view,keys){return this.ctxs.get(view).formatEntities(...keys);}},{key:'formatValues',value:function formatValues(view,keys){return this.ctxs.get(view).formatValues(...keys);}},{key:'changeLanguages',value:function changeLanguages(view,meta,additionalLangs,requestedLangs){const oldCtx=this.ctxs.get(view);const prevLangs=oldCtx.langs;const newLangs=negotiateLanguages(meta,additionalLangs,prevLangs,requestedLangs);this.ctxs.set(view,this.env.createContext(newLangs.langs,oldCtx.resIds));return newLangs;}},{key:'requestLanguages',value:function requestLanguages(requestedLangs){this.broadcast('languageschangerequest',requestedLangs);}},{key:'getName',value:function getName(code){return pseudo[code].name;}},{key:'processString',value:function processString(code,str){return pseudo[code].process(str);}}]);return Remote;})();const observerConfig={attributes:true,characterData:false,childList:true,subtree:true,attributeFilter:['data-l10n-id','data-l10n-args']};const observers=new WeakMap();function initMutationObserver(view){observers.set(view,{roots:new Set(),observer:new MutationObserver(mutations=>translateMutations(view,mutations))});}
|
||||
function translateRoots(view){return Promise.all([...observers.get(view).roots].map(root=>_translateFragment(view,root)));}
|
||||
function observe(view,root){const obs=observers.get(view);if(obs){obs.roots.add(root);obs.observer.observe(root,observerConfig);}}
|
||||
function disconnect(view,root,allRoots){const obs=observers.get(view);if(obs){obs.observer.disconnect();if(allRoots){return;}
|
||||
obs.roots.delete(root);obs.roots.forEach(other=>obs.observer.observe(other,observerConfig));}}
|
||||
function reconnect(view){const obs=observers.get(view);if(obs){obs.roots.forEach(root=>obs.observer.observe(root,observerConfig));}}
|
||||
const reOverlay=/<|&#?\w+;/;const allowed={elements:['a','em','strong','small','s','cite','q','dfn','abbr','data','time','code','var','samp','kbd','sub','sup','i','b','u','mark','ruby','rt','rp','bdi','bdo','span','br','wbr'],attributes:{global:['title','aria-label','aria-valuetext','aria-moz-hint'],a:['download'],area:['download','alt'],input:['alt','placeholder'],menuitem:['label'],menu:['label'],optgroup:['label'],option:['label'],track:['label'],img:['alt'],textarea:['placeholder'],th:['abbr']}};function overlayElement(element,translation){const value=translation.value;if(typeof value==='string'){if(!reOverlay.test(value)){element.textContent=value;}else{const tmpl=element.ownerDocument.createElement('template');tmpl.innerHTML=value;overlay(element,tmpl.content);}}
|
||||
for(let key in translation.attrs){const attrName=camelCaseToDashed(key);if(isAttrAllowed({name:attrName},element)){element.setAttribute(attrName,translation.attrs[key]);}}}
|
||||
function overlay(sourceElement,translationElement){const result=translationElement.ownerDocument.createDocumentFragment();let k,attr;let childElement;while(childElement=translationElement.childNodes[0]){translationElement.removeChild(childElement);if(childElement.nodeType===childElement.TEXT_NODE){result.appendChild(childElement);continue;}
|
||||
const index=getIndexOfType(childElement);const sourceChild=getNthElementOfType(sourceElement,childElement,index);if(sourceChild){overlay(sourceChild,childElement);result.appendChild(sourceChild);continue;}
|
||||
if(isElementAllowed(childElement)){const sanitizedChild=childElement.ownerDocument.createElement(childElement.nodeName);overlay(sanitizedChild,childElement);result.appendChild(sanitizedChild);continue;}
|
||||
result.appendChild(translationElement.ownerDocument.createTextNode(childElement.textContent));}
|
||||
sourceElement.textContent='';sourceElement.appendChild(result);if(translationElement.attributes){for(k=0,attr;attr=translationElement.attributes[k];k++){if(isAttrAllowed(attr,sourceElement)){sourceElement.setAttribute(attr.name,attr.value);}}}}
|
||||
function isElementAllowed(element){return allowed.elements.indexOf(element.tagName.toLowerCase())!==-1;}
|
||||
function isAttrAllowed(attr,element){const attrName=attr.name.toLowerCase();const tagName=element.tagName.toLowerCase();if(allowed.attributes.global.indexOf(attrName)!==-1){return true;}
|
||||
if(!allowed.attributes[tagName]){return false;}
|
||||
if(allowed.attributes[tagName].indexOf(attrName)!==-1){return true;}
|
||||
if(tagName==='input'&&attrName==='value'){const type=element.type.toLowerCase();if(type==='submit'||type==='button'||type==='reset'){return true;}}
|
||||
return false;}
|
||||
function getNthElementOfType(context,element,index){let nthOfType=0;for(let i=0,child;child=context.children[i];i++){if(child.nodeType===child.ELEMENT_NODE&&child.tagName===element.tagName){if(nthOfType===index){return child;}
|
||||
nthOfType++;}}
|
||||
return null;}
|
||||
function getIndexOfType(element){let index=0;let child;while(child=element.previousElementSibling){if(child.tagName===element.tagName){index++;}}
|
||||
return index;}
|
||||
function camelCaseToDashed(string){if(string==='ariaValueText'){return'aria-valuetext';}
|
||||
return string.replace(/[A-Z]/g,function(match){return'-'+match.toLowerCase();}).replace(/^-/,'');}
|
||||
const reHtml=/[&<>]/g;const htmlEntities={'&':'&','<':'<','>':'>'};function setAttributes(element,id,args){element.setAttribute('data-l10n-id',id);if(args){element.setAttribute('data-l10n-args',JSON.stringify(args));}}
|
||||
function getAttributes(element){return{id:element.getAttribute('data-l10n-id'),args:JSON.parse(element.getAttribute('data-l10n-args'))};}
|
||||
function getTranslatables(element){const nodes=Array.from(element.querySelectorAll('[data-l10n-id]'));if(typeof element.hasAttribute==='function'&&element.hasAttribute('data-l10n-id')){nodes.push(element);}
|
||||
return nodes;}
|
||||
function translateMutations(view,mutations){const targets=new Set();for(let mutation of mutations){switch(mutation.type){case'attributes':targets.add(mutation.target);break;case'childList':for(let addedNode of mutation.addedNodes){if(addedNode.nodeType===addedNode.ELEMENT_NODE){if(addedNode.childElementCount){getTranslatables(addedNode).forEach(targets.add.bind(targets));}else{if(addedNode.hasAttribute('data-l10n-id')){targets.add(addedNode);}}}}
|
||||
break;}}
|
||||
if(targets.size===0){return;}
|
||||
translateElements(view,Array.from(targets));}
|
||||
function _translateFragment(view,frag){return translateElements(view,getTranslatables(frag));}
|
||||
function getElementsTranslation(view,elems){const keys=elems.map(elem=>{const id=elem.getAttribute('data-l10n-id');const args=elem.getAttribute('data-l10n-args');return args?[id,JSON.parse(args.replace(reHtml,match=>htmlEntities[match]))]:id;});return view.formatEntities(...keys);}
|
||||
function translateElements(view,elements){return getElementsTranslation(view,elements).then(translations=>applyTranslations(view,elements,translations));}
|
||||
function applyTranslations(view,elems,translations){disconnect(view,null,true);for(let i=0;i<elems.length;i++){overlayElement(elems[i],translations[i]);}
|
||||
reconnect(view);}
|
||||
if(typeof NodeList==='function'&&!NodeList.prototype[Symbol.iterator]){NodeList.prototype[Symbol.iterator]=Array.prototype[Symbol.iterator];}
|
||||
function documentReady(){if(document.readyState!=='loading'){return Promise.resolve();}
|
||||
return new Promise(resolve=>{document.addEventListener('readystatechange',function onrsc(){document.removeEventListener('readystatechange',onrsc);resolve();});});}
|
||||
function getDirection(code){const tag=code.split('-')[0];return['ar','he','fa','ps','ur'].indexOf(tag)>=0?'rtl':'ltr';}
|
||||
if(navigator.languages===undefined){navigator.languages=[navigator.language];}
|
||||
function getResourceLinks(head){return Array.prototype.map.call(head.querySelectorAll('link[rel="localization"]'),el=>el.getAttribute('href'));}
|
||||
function getMeta(head){let availableLangs=Object.create(null);let defaultLang=null;let appVersion=null;const metas=Array.from(head.querySelectorAll('meta[name="availableLanguages"],'+'meta[name="defaultLanguage"],'+'meta[name="appVersion"]'));for(let meta of metas){const name=meta.getAttribute('name');const content=meta.getAttribute('content').trim();switch(name){case'availableLanguages':availableLangs=getLangRevisionMap(availableLangs,content);break;case'defaultLanguage':const[lang,rev]=getLangRevisionTuple(content);defaultLang=lang;if(!(lang in availableLangs)){availableLangs[lang]=rev;}
|
||||
break;case'appVersion':appVersion=content;}}
|
||||
return{defaultLang,availableLangs,appVersion};}
|
||||
function getLangRevisionMap(seq,str){return str.split(',').reduce((seq,cur)=>{const[lang,rev]=getLangRevisionTuple(cur);seq[lang]=rev;return seq;},seq);}
|
||||
function getLangRevisionTuple(str){const[lang,rev]=str.trim().split(':');return[lang,parseInt(rev)];}
|
||||
const viewProps=new WeakMap();let View=(function(){function View(client,doc){_classCallCheck(this,View);this.pseudo={'fr-x-psaccent':createPseudo(this,'fr-x-psaccent'),'ar-x-psbidi':createPseudo(this,'ar-x-psbidi')};const initialized=documentReady().then(()=>init(this,client));this._interactive=initialized.then(()=>client);this.ready=initialized.then(langs=>translateView(this,langs));initMutationObserver(this);viewProps.set(this,{doc:doc,ready:false});client.on('languageschangerequest',requestedLangs=>this.requestLanguages(requestedLangs));}
|
||||
_createClass(View,[{key:'requestLanguages',value:function requestLanguages(requestedLangs,isGlobal){const method=isGlobal?client=>client.method('requestLanguages',requestedLangs):client=>changeLanguages(this,client,requestedLangs);return this._interactive.then(method);}},{key:'handleEvent',value:function handleEvent(){return this.requestLanguages(navigator.languages);}},{key:'formatEntities',value:function formatEntities(...keys){return this._interactive.then(client=>client.method('formatEntities',client.id,keys));}},{key:'formatValue',value:function formatValue(id,args){return this._interactive.then(client=>client.method('formatValues',client.id,[[id,args]])).then(values=>values[0]);}},{key:'formatValues',value:function formatValues(...keys){return this._interactive.then(client=>client.method('formatValues',client.id,keys));}},{key:'translateFragment',value:function translateFragment(frag){return _translateFragment(this,frag);}},{key:'observeRoot',value:function observeRoot(root){observe(this,root);}},{key:'disconnectRoot',value:function disconnectRoot(root){disconnect(this,root);}}]);return View;})();View.prototype.setAttributes=setAttributes;View.prototype.getAttributes=getAttributes;function createPseudo(view,code){return{getName:()=>view._interactive.then(client=>client.method('getName',code)),processString:str=>view._interactive.then(client=>client.method('processString',code,str))};}
|
||||
function init(view,client){const doc=viewProps.get(view).doc;const resources=getResourceLinks(doc.head);const meta=getMeta(doc.head);view.observeRoot(doc.documentElement);return getAdditionalLanguages().then(additionalLangs=>client.method('registerView',client.id,resources,meta,additionalLangs,navigator.languages));}
|
||||
function changeLanguages(view,client,requestedLangs){const doc=viewProps.get(view).doc;const meta=getMeta(doc.head);return getAdditionalLanguages().then(additionalLangs=>client.method('changeLanguages',client.id,meta,additionalLangs,requestedLangs)).then(({langs,haveChanged})=>haveChanged?translateView(view,langs):undefined);}
|
||||
function getAdditionalLanguages(){if(navigator.mozApps&&navigator.mozApps.getAdditionalLanguages){return navigator.mozApps.getAdditionalLanguages().catch(()=>Object.create(null));}
|
||||
return Promise.resolve(Object.create(null));}
|
||||
function translateView(view,langs){const props=viewProps.get(view);const html=props.doc.documentElement;if(props.ready){return translateRoots(view).then(()=>setAllAndEmit(html,langs));}
|
||||
const translated=langs[0].code===html.getAttribute('lang')?Promise.resolve():translateRoots(view).then(()=>setLangDir(html,langs));return translated.then(()=>{setLangs(html,langs);props.ready=true;});}
|
||||
function setLangs(html,langs){const codes=langs.map(lang=>lang.code);html.setAttribute('langs',codes.join(' '));}
|
||||
function setLangDir(html,langs){const code=langs[0].code;html.setAttribute('lang',code);html.setAttribute('dir',getDirection(code));}
|
||||
function setAllAndEmit(html,langs){setLangDir(html,langs);setLangs(html,langs);html.parentNode.dispatchEvent(new CustomEvent('DOMRetranslated',{bubbles:false,cancelable:false}));}
|
||||
const remote=new Remote(fetchResource,broadcast);const client=new Client(remote);document.l10n=new View(client,document);window.addEventListener('languagechange',document.l10n);document.addEventListener('additionallanguageschange',document.l10n);navigator.mozL10n={setAttributes:document.l10n.setAttributes,getAttributes:document.l10n.getAttributes,formatValue:(...args)=>document.l10n.formatValue(...args),translateFragment:(...args)=>document.l10n.translateFragment(...args),once:cb=>document.l10n.ready.then(cb),ready:cb=>document.l10n.ready.then(()=>{document.addEventListener('DOMRetranslated',cb);cb();})};})();
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
;'use strict';(function(exports){var BASE_TYPES=new Set(['text','url','email','password','number','option']);var SETTINGS_KEYS={ENABLED:'keyboard.enabled-layouts',DEFAULT:'keyboard.default-layouts'};var DEPRECATE_KEYBOARD_SETTINGS={en:'keyboard.layouts.english','en-Dvorak':'keyboard.layouts.dvorak',cs:'keyboard.layouts.czech',fr:'keyboard.layouts.french',de:'keyboard.layouts.german',hu:'keyboard.layouts.hungarian',nb:'keyboard.layouts.norwegian',my:'keyboard.layouts.myanmar',sl:'keyboard.layouts.slovak',tr:'keyboard.layouts.turkish',ro:'keyboard.layouts.romanian',ru:'keyboard.layouts.russian',ar:'keyboard.layouts.arabic',he:'keyboard.layouts.hebrew','zh-Hant-Zhuyin':'keyboard.layouts.zhuyin','zh-Hans-Pinyin':'keyboard.layouts.pinyin',el:'keyboard.layouts.greek','jp-kanji':'keyboard.layouts.japanese',pl:'keyboard.layouts.polish','pt-BR':'keyboard.layouts.portuguese',sr:'keyboard.layouts.serbian',es:'keyboard.layouts.spanish',ca:'keyboard.layouts.catalan'};var MULTI_LAYOUT_MAP={sr:['sr-Cyrl','sr-Latn']};var defaultKeyboardManifestURL='app://keyboard.gaiamobile.org/manifest.webapp';var currentSettings={defaultLayouts:{}};currentSettings.defaultLayouts[defaultKeyboardManifestURL]={en:true,number:true};currentSettings.enabledLayouts=map2dClone(currentSettings.defaultLayouts);function map2dIs(manifestURL,layoutId){return!!(this[manifestURL]&&this[manifestURL][layoutId]);}
|
||||
function map2dSet(manifestURL,layoutId){if(!this[manifestURL]){this[manifestURL]={};}
|
||||
this[manifestURL][layoutId]=true;}
|
||||
function map2dUnset(manifestURL,layoutId){if(!this[manifestURL]){return;}
|
||||
delete this[manifestURL][layoutId];if(!Object.keys(this[manifestURL]).length){delete this[manifestURL];}}
|
||||
function map2dClone(obj){var result={};for(var key1 in obj){result[key1]={};for(var key2 in obj[key1]){result[key1][key2]=obj[key1][key2];}}
|
||||
return result;}
|
||||
var watchQueries=[];var defaultLayoutConfig;function kh_updateWatchers(reason){if(watchQueries.length){KeyboardHelper.getApps(function withApps(){watchQueries.forEach(function eachWatchQuery(watch){KeyboardHelper.getLayouts(watch.query,function withLayouts(layouts){watch.callback(layouts,reason);});});});}}
|
||||
var waitingForSettings=[];var loadedSettings=new Set();function kh_loadedSetting(setting){loadedSettings.add(setting);if(loadedSettings.size>=Object.keys(SETTINGS_KEYS).length){waitingForSettings.forEach(function(callback){callback();});waitingForSettings=[];kh_updateWatchers({settings:true});}}
|
||||
function kh_withSettings(callback){if(loadedSettings.size>=Object.keys(SETTINGS_KEYS).length){callback();}else{waitingForSettings.push(callback);}}
|
||||
function kh_getSettings(){loadedSettings=new Set();var lock=window.navigator.mozSettings.createLock();lock.get(SETTINGS_KEYS.DEFAULT).onsuccess=kh_parseDefault;lock.get(SETTINGS_KEYS.ENABLED).onsuccess=kh_parseEnabled;}
|
||||
function kh_parseDefault(){var value=this.result[SETTINGS_KEYS.DEFAULT];if(value){currentSettings.defaultLayouts=value;}
|
||||
kh_loadedSetting(SETTINGS_KEYS.DEFAULT);}
|
||||
function kh_parseEnabled(){var value=this.result[SETTINGS_KEYS.ENABLED];var needsSave=false;if(!value){currentSettings.enabledLayouts=map2dClone(currentSettings.defaultLayouts);needsSave=true;}else{var type=typeof value;if(type==='string'){try{currentSettings.enabledLayouts={};var oldSettings=JSON.parse(value);oldSettings.forEach(function(layout){if(layout.enabled){var manifestURL=layout.manifestURL;if(!manifestURL){manifestURL=layout.appOrigin+'/manifest.webapp';}
|
||||
map2dSet.call(currentSettings.enabledLayouts,manifestURL,layout.layoutId);}});}catch(e){currentSettings.enabledLayouts=map2dClone(currentSettings.defaultLayouts);needsSave=true;}}else{currentSettings.enabledLayouts=value;}}
|
||||
kh_loadedSetting(SETTINGS_KEYS.ENABLED);if(needsSave){KeyboardHelper.saveToSettings();}}
|
||||
function kh_migrateDeprecatedSettings(deprecatedSettings){var settingEntry=DEPRECATE_KEYBOARD_SETTINGS.en;if(deprecatedSettings[settingEntry]===undefined){return;}
|
||||
currentSettings.enabledLayouts[defaultKeyboardManifestURL]={number:true};var hasEnabledLayout=false;for(var key in DEPRECATE_KEYBOARD_SETTINGS){settingEntry=DEPRECATE_KEYBOARD_SETTINGS[key];if(deprecatedSettings[settingEntry]){hasEnabledLayout=true;if(key in MULTI_LAYOUT_MAP){MULTI_LAYOUT_MAP[key].forEach(function enableLayout(layoutId){map2dSet.call(currentSettings.enabledLayouts,defaultKeyboardManifestURL,layoutId);});}else{map2dSet.call(currentSettings.enabledLayouts,defaultKeyboardManifestURL,key);}}}
|
||||
if(!hasEnabledLayout){map2dSet.call(currentSettings.enabledLayouts,defaultKeyboardManifestURL,'en');}
|
||||
var deprecatedSettingsQuery={};for(key in DEPRECATE_KEYBOARD_SETTINGS){settingEntry=DEPRECATE_KEYBOARD_SETTINGS[key];deprecatedSettingsQuery[settingEntry]=null;}
|
||||
window.navigator.mozSettings.createLock().set(deprecatedSettingsQuery);KeyboardHelper.saveToSettings();}
|
||||
function kh_loadJSON(href,callback){if(!callback){return;}
|
||||
var xhr=new XMLHttpRequest();xhr.onerror=function(){console.error('Failed to fetch file: '+href,xhr.statusText);};xhr.onload=function(){callback(xhr.response);};xhr.open('GET',href,true);xhr.responseType='json';xhr.send();}
|
||||
function kh_getMultiSettings(settings,callback){var results={};try{var lock=navigator.mozSettings.createLock();}
|
||||
catch(e){console.warn('Exception in mozSettings.createLock():',e,'\nUsing default values');for(var p in settings){results[p]=settings[p];}
|
||||
callback(results);}
|
||||
var settingNames=Object.keys(settings);var numSettings=settingNames.length;var numResults=0;for(var i=0;i<numSettings;i++){requestSetting(settingNames[i]);}
|
||||
function requestSetting(name){var request;try{request=lock.get(name);}
|
||||
catch(e){console.warn('Exception querying setting',name,':',e,'\nUsing default value');recordResult(name,settings[name]);return;}
|
||||
request.onsuccess=function(){var value=request.result[name];if(value===undefined){value=settings[name];}
|
||||
recordResult(name,value);};request.onerror=function(evt){console.warn('Error querying setting',name,':',evt.error);recordResult(name,settings[name]);};}
|
||||
function recordResult(name,value){results[name]=value;numResults++;if(numResults===numSettings){callback(results);}}}
|
||||
function KeyboardLayout(options){for(var key in options){this[key]=options[key];}}
|
||||
Object.defineProperties(KeyboardLayout.prototype,{'default':{get:function kh_getLayoutIsDefault(){return map2dIs.call(currentSettings.defaultLayouts,this.app.manifestURL,this.layoutId);}},enabled:{get:function kh_getLayoutIsEnabled(){return map2dIs.call(currentSettings.enabledLayouts,this.app.manifestURL,this.layoutId);},set:function kh_setLayoutIsDefault(value){var method=value?map2dSet:map2dUnset;method.call(currentSettings.enabledLayouts,this.app.manifestURL,this.layoutId);}}});var kh_SettingsHelper={};Object.defineProperties(kh_SettingsHelper,{'default':{get:function(){return map2dClone(currentSettings.defaultLayouts);},set:function(value){currentSettings.defaultLayouts=map2dClone(value);},enumerable:true},'enabled':{get:function(){return map2dClone(currentSettings.enabledLayouts);},set:function(value){currentSettings.enabledLayouts=map2dClone(value);},enumerable:true}});var KeyboardHelper=exports.KeyboardHelper={settings:kh_SettingsHelper,fallbackLayoutNames:{password:'en'},fallbackLayouts:{},inputAppList:null,init:function kh_init(){watchQueries=[];this.inputAppList=new InputAppList();this.inputAppList.onupdate=kh_updateWatchers.bind(undefined,{apps:true});this.inputAppList.start();var settings=window.navigator.mozSettings;if(settings){kh_getSettings();settings.addObserver(SETTINGS_KEYS.ENABLED,kh_getSettings);settings.addObserver(SETTINGS_KEYS.DEFAULT,kh_getSettings);var deprecatedSettingsQuery={};for(var key in DEPRECATE_KEYBOARD_SETTINGS){var settingEntry=DEPRECATE_KEYBOARD_SETTINGS[key];deprecatedSettingsQuery[settingEntry]=undefined;}
|
||||
kh_getMultiSettings(deprecatedSettingsQuery,kh_migrateDeprecatedSettings);}
|
||||
window.addEventListener('applicationinstallsuccess',this);},handleEvent:function(event){kh_updateWatchers({apps:true});},isKeyboardType:function kh_isKeyboardType(type){return BASE_TYPES.has(type);},setLayoutEnabled:function kh_setLayoutEnabled(manifestURL,layoutId,enabled){var method=enabled?map2dSet:map2dUnset;method.call(currentSettings.enabledLayouts,manifestURL,layoutId);},getLayoutEnabled:function kh_getLayoutEnabled(manifestURL,layoutId){return map2dIs.call(currentSettings.enabledLayouts,manifestURL,layoutId);},setLayoutIsDefault:function kh_setLayoutIsDefault(manifestURL,layoutId,enabled){var method=enabled?map2dSet:map2dUnset;method.call(currentSettings.defaultLayouts,manifestURL,layoutId);},getLayoutIsDefault:function kh_getLayoutIsDefault(manifestURL,layoutId){return map2dIs.call(currentSettings.defaultLayouts,manifestURL,layoutId);},checkDefaults:function kh_checkDefaults(callback){var layoutsEnabled=[];var missingTypes=[];['text','url','number'].forEach(function eachType(type){var enabled;this.getLayouts({type:type,enabled:true},function(layouts){enabled=layouts.length;});if(!enabled){missingTypes.push(type);this.getLayouts({type:type,'default':true},function(layouts){if(layouts[0]){layouts[0].enabled=true;layoutsEnabled.push(layouts[0]);}});}},this);if(layoutsEnabled.length){if(callback){callback(layoutsEnabled,missingTypes);}}},saveToSettings:function ke_saveToSettings(){var toSet={};toSet[SETTINGS_KEYS.ENABLED]=currentSettings.enabledLayouts;toSet[SETTINGS_KEYS.DEFAULT]=currentSettings.defaultLayouts;window.navigator.mozSettings.createLock().set(toSet);},getApps:function kh_getApps(callback){var cleanupSettings=function(inputApps){Object.keys(currentSettings.enabledLayouts).concat(Object.keys(currentSettings.defaultLayouts)).forEach(function(manifestURL){if(!inputApps.some(function(inputApp){return(inputApp.domApp.manifestURL===manifestURL);})){delete currentSettings.enabledLayouts[manifestURL];delete currentSettings.defaultLayouts[manifestURL];}});};if(this.inputAppList.ready){var inputApps=this.inputAppList.getListSync();cleanupSettings(inputApps);callback(inputApps);return;}
|
||||
this.inputAppList.getList().then(function(inputApps){cleanupSettings(inputApps);callback(inputApps);})['catch'](function(e){console.error(e);});},getLayouts:function kh_getLayouts(options,callback){if(typeof options==='function'){callback=options;options={};}
|
||||
function withApps(inputApps){var fallbackLayoutNames={};for(var group in this.fallbackLayoutNames){fallbackLayoutNames[group]=this.fallbackLayoutNames[group];}
|
||||
this.fallbackLayouts={};var layouts=inputApps.reduce(function eachApp(result,inputApp){var domApp=inputApp.domApp;var manifest=new ManifestHelper(domApp.manifest);var inputs=inputApp.getInputs();for(var layoutId in inputs){var inputManifest=inputs[layoutId];if(!inputManifest.types){console.warn(domApp.manifestURL,layoutId,'did not declare type.');continue;}
|
||||
var layout=new KeyboardLayout({app:domApp,inputApp:inputApp,manifest:manifest,inputManifest:inputManifest,layoutId:layoutId});if(domApp.manifestURL===defaultKeyboardManifestURL){for(var group in fallbackLayoutNames){if(layoutId===fallbackLayoutNames[group]){this.fallbackLayouts[group]=layout;delete fallbackLayoutNames[group];}}}
|
||||
if(options['default']&&!layout['default']){continue;}
|
||||
if(options.enabled&&!layout.enabled){continue;}
|
||||
if(options.type){options.type=[].concat(options.type);if(!options.type.some(function(type){return inputManifest.types.indexOf(type)!==-1;})){continue;}}
|
||||
result.push(layout);}
|
||||
if(options['default']){result.sort(function(a,b){return a.inputManifest.types.length-b.inputManifest.types.length;});}
|
||||
return result;}.bind(this),[]);kh_withSettings(callback.bind(null,layouts));}
|
||||
this.getApps(withApps.bind(this));},stopWatching:function(){watchQueries=[];},watchLayouts:function(query,callback){if(typeof query==='function'){callback=query;query={};}
|
||||
var watch={query:query,callback:callback};watchQueries.push(watch);this.getLayouts(query,function initialCall(layouts){watch.layouts=layouts;callback(layouts,{apps:true,settings:true});});},getDefaultLayoutConfig:function kh_getDefaultLayoutConfig(callback){if(!callback){return;}
|
||||
if(defaultLayoutConfig){callback(defaultLayoutConfig);}else{var KEYBOARDS='/shared/resources/keyboard_layouts.json';kh_loadJSON(KEYBOARDS,function loadKeyboardLayouts(data){if(data){defaultLayoutConfig=data;callback(defaultLayoutConfig);}});}},changeDefaultLayouts:function kh_changeDefaultLayouts(language,reset){this.getDefaultLayoutConfig(function gotDefaultLayouts(keyboards){var newKbLayouts=keyboards.layout[language];var kbManifestURL=defaultKeyboardManifestURL;currentSettings.defaultLayouts={};var langIndependentLayouts=keyboards.langIndependentLayouts;for(var i=langIndependentLayouts.length-1;i>=0;i--){this.setLayoutIsDefault(kbManifestURL,langIndependentLayouts[i].layoutId,true);}
|
||||
if(reset){currentSettings.enabledLayouts=map2dClone(currentSettings.defaultLayouts);}
|
||||
for(i=newKbLayouts.length-1;i>=0;i--){this.setLayoutIsDefault(kbManifestURL,newKbLayouts[i].layoutId,true);this.setLayoutEnabled(kbManifestURL,newKbLayouts[i].layoutId,true);}
|
||||
this.saveToSettings();}.bind(this));}};KeyboardHelper.init();}(window));
|
||||
245
assets/webapps/ftu.gaiamobile.org/shared/js/l10n.js
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
;;;(function(window,undefined){'use strict';function L10nError(message,id,loc){this.name='L10nError';this.message=message;this.id=id;this.loc=loc;}
|
||||
L10nError.prototype=Object.create(Error.prototype);L10nError.prototype.constructor=L10nError;var io={_load:function(type,url,callback,sync){var xhr=new XMLHttpRequest();var needParse;if(xhr.overrideMimeType){xhr.overrideMimeType(type);}
|
||||
xhr.open('GET',url,!sync);if(type==='application/json'){if(sync){needParse=true;}else{xhr.responseType='json';}}
|
||||
xhr.addEventListener('load',function io_onload(e){if(e.target.status===200||e.target.status===0){var res=e.target.response||e.target.responseText;callback(null,needParse?JSON.parse(res):res);}else{callback(new L10nError('Not found: '+url));}});xhr.addEventListener('error',callback);xhr.addEventListener('timeout',callback);try{xhr.send(null);}catch(e){if(e.name==='NS_ERROR_FILE_NOT_FOUND'){callback(new L10nError('Not found: '+url));}else{throw e;}}},load:function(url,callback,sync){return io._load('text/plain',url,callback,sync);},loadJSON:function(url,callback,sync){return io._load('application/json',url,callback,sync);}};function EventEmitter(){}
|
||||
EventEmitter.prototype.emit=function ee_emit(){if(!this._listeners){return;}
|
||||
var args=Array.prototype.slice.call(arguments);var type=args.shift();if(!this._listeners[type]){return;}
|
||||
var typeListeners=this._listeners[type].slice();for(var i=0;i<typeListeners.length;i++){typeListeners[i].apply(this,args);}};EventEmitter.prototype.addEventListener=function ee_add(type,listener){if(!this._listeners){this._listeners={};}
|
||||
if(!(type in this._listeners)){this._listeners[type]=[];}
|
||||
this._listeners[type].push(listener);};EventEmitter.prototype.removeEventListener=function ee_rm(type,listener){if(!this._listeners){return;}
|
||||
var typeListeners=this._listeners[type];var pos=typeListeners.indexOf(listener);if(pos===-1){return;}
|
||||
typeListeners.splice(pos,1);};function getPluralRule(lang){var locales2rules={'af':3,'ak':4,'am':4,'ar':1,'asa':3,'az':0,'be':11,'bem':3,'bez':3,'bg':3,'bh':4,'bm':0,'bn':3,'bo':0,'br':20,'brx':3,'bs':11,'ca':3,'cgg':3,'chr':3,'cs':12,'cy':17,'da':3,'de':3,'dv':3,'dz':0,'ee':3,'el':3,'en':3,'eo':3,'es':3,'et':3,'eu':3,'fa':0,'ff':5,'fi':3,'fil':4,'fo':3,'fr':5,'fur':3,'fy':3,'ga':8,'gd':24,'gl':3,'gsw':3,'gu':3,'guw':4,'gv':23,'ha':3,'haw':3,'he':2,'hi':4,'hr':11,'hu':0,'id':0,'ig':0,'ii':0,'is':3,'it':3,'iu':7,'ja':0,'jmc':3,'jv':0,'ka':0,'kab':5,'kaj':3,'kcg':3,'kde':0,'kea':0,'kk':3,'kl':3,'km':0,'kn':0,'ko':0,'ksb':3,'ksh':21,'ku':3,'kw':7,'lag':18,'lb':3,'lg':3,'ln':4,'lo':0,'lt':10,'lv':6,'mas':3,'mg':4,'mk':16,'ml':3,'mn':3,'mo':9,'mr':3,'ms':0,'mt':15,'my':0,'nah':3,'naq':7,'nb':3,'nd':3,'ne':3,'nl':3,'nn':3,'no':3,'nr':3,'nso':4,'ny':3,'nyn':3,'om':3,'or':3,'pa':3,'pap':3,'pl':13,'ps':3,'pt':3,'rm':3,'ro':9,'rof':3,'ru':11,'rwk':3,'sah':0,'saq':3,'se':7,'seh':3,'ses':0,'sg':0,'sh':11,'shi':19,'sk':12,'sl':14,'sma':7,'smi':7,'smj':7,'smn':7,'sms':7,'sn':3,'so':3,'sq':3,'sr':11,'ss':3,'ssy':3,'st':3,'sv':3,'sw':3,'syr':3,'ta':3,'te':3,'teo':3,'th':0,'ti':4,'tig':3,'tk':3,'tl':4,'tn':3,'to':0,'tr':0,'ts':3,'tzm':22,'uk':11,'ur':3,'ve':3,'vi':0,'vun':3,'wa':4,'wae':3,'wo':0,'xh':3,'xog':3,'yo':0,'zh':0,'zu':3};function isIn(n,list){return list.indexOf(n)!==-1;}
|
||||
function isBetween(n,start,end){return typeof n===typeof start&&start<=n&&n<=end;}
|
||||
var pluralRules={'0':function(){return'other';},'1':function(n){if((isBetween((n%100),3,10))){return'few';}
|
||||
if(n===0){return'zero';}
|
||||
if((isBetween((n%100),11,99))){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'2':function(n){if(n!==0&&(n%10)===0){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'3':function(n){if(n===1){return'one';}
|
||||
return'other';},'4':function(n){if((isBetween(n,0,1))){return'one';}
|
||||
return'other';},'5':function(n){if((isBetween(n,0,2))&&n!==2){return'one';}
|
||||
return'other';},'6':function(n){if(n===0){return'zero';}
|
||||
if((n%10)===1&&(n%100)!==11){return'one';}
|
||||
return'other';},'7':function(n){if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'8':function(n){if((isBetween(n,3,6))){return'few';}
|
||||
if((isBetween(n,7,10))){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'9':function(n){if(n===0||n!==1&&(isBetween((n%100),1,19))){return'few';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'10':function(n){if((isBetween((n%10),2,9))&&!(isBetween((n%100),11,19))){return'few';}
|
||||
if((n%10)===1&&!(isBetween((n%100),11,19))){return'one';}
|
||||
return'other';},'11':function(n){if((isBetween((n%10),2,4))&&!(isBetween((n%100),12,14))){return'few';}
|
||||
if((n%10)===0||(isBetween((n%10),5,9))||(isBetween((n%100),11,14))){return'many';}
|
||||
if((n%10)===1&&(n%100)!==11){return'one';}
|
||||
return'other';},'12':function(n){if((isBetween(n,2,4))){return'few';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'13':function(n){if((isBetween((n%10),2,4))&&!(isBetween((n%100),12,14))){return'few';}
|
||||
if(n!==1&&(isBetween((n%10),0,1))||(isBetween((n%10),5,9))||(isBetween((n%100),12,14))){return'many';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'14':function(n){if((isBetween((n%100),3,4))){return'few';}
|
||||
if((n%100)===2){return'two';}
|
||||
if((n%100)===1){return'one';}
|
||||
return'other';},'15':function(n){if(n===0||(isBetween((n%100),2,10))){return'few';}
|
||||
if((isBetween((n%100),11,19))){return'many';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'16':function(n){if((n%10)===1&&n!==11){return'one';}
|
||||
return'other';},'17':function(n){if(n===3){return'few';}
|
||||
if(n===0){return'zero';}
|
||||
if(n===6){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'18':function(n){if(n===0){return'zero';}
|
||||
if((isBetween(n,0,2))&&n!==0&&n!==2){return'one';}
|
||||
return'other';},'19':function(n){if((isBetween(n,2,10))){return'few';}
|
||||
if((isBetween(n,0,1))){return'one';}
|
||||
return'other';},'20':function(n){if((isBetween((n%10),3,4)||((n%10)===9))&&!(isBetween((n%100),10,19)||isBetween((n%100),70,79)||isBetween((n%100),90,99))){return'few';}
|
||||
if((n%1000000)===0&&n!==0){return'many';}
|
||||
if((n%10)===2&&!isIn((n%100),[12,72,92])){return'two';}
|
||||
if((n%10)===1&&!isIn((n%100),[11,71,91])){return'one';}
|
||||
return'other';},'21':function(n){if(n===0){return'zero';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'22':function(n){if((isBetween(n,0,1))||(isBetween(n,11,99))){return'one';}
|
||||
return'other';},'23':function(n){if((isBetween((n%10),1,2))||(n%20)===0){return'one';}
|
||||
return'other';},'24':function(n){if((isBetween(n,3,10)||isBetween(n,13,19))){return'few';}
|
||||
if(isIn(n,[2,12])){return'two';}
|
||||
if(isIn(n,[1,11])){return'one';}
|
||||
return'other';}};var index=locales2rules[lang.replace(/-.*$/,'')];if(!(index in pluralRules)){return function(){return'other';};}
|
||||
return pluralRules[index];}
|
||||
var MAX_PLACEABLES=100;var PropertiesParser={patterns:null,entryIds:null,init:function(){this.patterns={comment:/^\s*#|^\s*$/,entity:/^([^=\s]+)\s*=\s*(.*)$/,multiline:/[^\\]\\$/,index:/\{\[\s*(\w+)(?:\(([^\)]*)\))?\s*\]\}/i,unicode:/\\u([0-9a-fA-F]{1,4})/g,entries:/[^\r\n]+/g,controlChars:/\\([\\\n\r\t\b\f\{\}\"\'])/g,placeables:/\{\{\s*([^\s]*?)\s*\}\}/,};},parse:function(ctx,source){if(!this.patterns){this.init();}
|
||||
var ast=[];this.entryIds=Object.create(null);var entries=source.match(this.patterns.entries);if(!entries){return ast;}
|
||||
for(var i=0;i<entries.length;i++){var line=entries[i];if(this.patterns.comment.test(line)){continue;}
|
||||
while(this.patterns.multiline.test(line)&&i<entries.length){line=line.slice(0,-1)+entries[++i].trim();}
|
||||
var entityMatch=line.match(this.patterns.entity);if(entityMatch){try{this.parseEntity(entityMatch[1],entityMatch[2],ast);}catch(e){if(ctx){ctx._emitter.emit('parseerror',e);}else{throw e;}}}}
|
||||
return ast;},parseEntity:function(id,value,ast){var name,key;var pos=id.indexOf('[');if(pos!==-1){name=id.substr(0,pos);key=id.substring(pos+1,id.length-1);}else{name=id;key=null;}
|
||||
var nameElements=name.split('.');if(nameElements.length>2){throw new L10nError('Error in ID: "'+name+'".'+' Nested attributes are not supported.');}
|
||||
var attr;if(nameElements.length>1){name=nameElements[0];attr=nameElements[1];if(attr[0]==='$'){throw new L10nError('Attribute can\'t start with "$"',id);}}else{attr=null;}
|
||||
this.setEntityValue(name,attr,key,this.unescapeString(value),ast);},setEntityValue:function(id,attr,key,rawValue,ast){var pos,v;var value=rawValue.indexOf('{{')>-1?this.parseString(rawValue):rawValue;if(attr){pos=this.entryIds[id];if(pos===undefined){v={$i:id};if(key){v[attr]={};v[attr][key]=value;}else{v[attr]=value;}
|
||||
ast.push(v);this.entryIds[id]=ast.length-1;return;}
|
||||
if(key){if(typeof(ast[pos][attr])==='string'){ast[pos][attr]={$x:this.parseIndex(ast[pos][attr]),$v:{}};}
|
||||
ast[pos][attr].$v[key]=value;return;}
|
||||
ast[pos][attr]=value;return;}
|
||||
if(key){pos=this.entryIds[id];if(pos===undefined){v={};v[key]=value;ast.push({$i:id,$v:v});this.entryIds[id]=ast.length-1;return;}
|
||||
if(typeof(ast[pos].$v)==='string'){ast[pos].$x=this.parseIndex(ast[pos].$v);ast[pos].$v={};}
|
||||
ast[pos].$v[key]=value;return;}
|
||||
ast.push({$i:id,$v:value});this.entryIds[id]=ast.length-1;},parseString:function(str){var chunks=str.split(this.patterns.placeables);var complexStr=[];var len=chunks.length;var placeablesCount=(len-1)/2;if(placeablesCount>=MAX_PLACEABLES){throw new L10nError('Too many placeables ('+placeablesCount+', max allowed is '+MAX_PLACEABLES+')');}
|
||||
for(var i=0;i<chunks.length;i++){if(chunks[i].length===0){continue;}
|
||||
if(i%2===1){complexStr.push({t:'idOrVar',v:chunks[i]});}else{complexStr.push(chunks[i]);}}
|
||||
return complexStr;},unescapeString:function(str){if(str.lastIndexOf('\\')!==-1){str=str.replace(this.patterns.controlChars,'$1');}
|
||||
return str.replace(this.patterns.unicode,function(match,token){return unescape('%u'+'0000'.slice(token.length)+token);});},parseIndex:function(str){var match=str.match(this.patterns.index);if(!match){throw new L10nError('Malformed index');}
|
||||
if(match[2]){return[{t:'idOrVar',v:match[1]},match[2]];}else{return[{t:'idOrVar',v:match[1]}];}}};var KNOWN_MACROS=['plural'];var MAX_PLACEABLE_LENGTH=2500;var rePlaceables=/\{\{\s*(.+?)\s*\}\}/g;var nonLatin1=/[^\x01-\xFF]/;var FSI='\u2068';var PDI='\u2069';function createEntry(node,env){var keys=Object.keys(node);if(typeof node.$v==='string'&&keys.length===2){return node.$v;}
|
||||
var attrs;for(var i=0,key;key=keys[i];i++){if(key[0]==='$'){continue;}
|
||||
if(!attrs){attrs=Object.create(null);}
|
||||
attrs[key]=createAttribute(node[key],env,node.$i+'.'+key);}
|
||||
return{id:node.$i,value:node.$v!==undefined?node.$v:null,index:node.$x||null,attrs:attrs||null,env:env,dirty:false};}
|
||||
function createAttribute(node,env,id){if(typeof node==='string'){return node;}
|
||||
return{id:id,value:node.$v||(node!==undefined?node:null),index:node.$x||null,env:env,dirty:false};}
|
||||
function format(args,entity){if(typeof entity==='string'){return[{},entity];}
|
||||
if(entity.dirty){throw new L10nError('Cyclic reference detected: '+entity.id);}
|
||||
entity.dirty=true;var rv;try{rv=resolveValue({},args,entity.env,entity.value,entity.index);}finally{entity.dirty=false;}
|
||||
return rv;}
|
||||
function resolveIdentifier(args,env,id){if(KNOWN_MACROS.indexOf(id)>-1){return[{},env['__'+id]];}
|
||||
if(args&&args.hasOwnProperty(id)){if(typeof args[id]==='string'||(typeof args[id]==='number'&&!isNaN(args[id]))){return[{},args[id]];}else{throw new L10nError('Arg must be a string or a number: '+id);}}
|
||||
if(id in env&&id!=='__proto__'){return format(args,env[id]);}
|
||||
throw new L10nError('Unknown reference: '+id);}
|
||||
function subPlaceable(locals,args,env,id){var res;try{res=resolveIdentifier(args,env,id);}catch(err){return[{error:err},'{{ '+id+' }}'];}
|
||||
var value=res[1];if(typeof value==='number'){return res;}
|
||||
if(typeof value==='string'){if(value.length>=MAX_PLACEABLE_LENGTH){throw new L10nError('Too many characters in placeable ('+
|
||||
value.length+', max allowed is '+
|
||||
MAX_PLACEABLE_LENGTH+')');}
|
||||
if(locals.contextIsNonLatin1||value.match(nonLatin1)){res[1]=FSI+value+PDI;}
|
||||
return res;}
|
||||
return[{},'{{ '+id+' }}'];}
|
||||
function interpolate(locals,args,env,arr){return arr.reduce(function(prev,cur){if(typeof cur==='string'){return[prev[0],prev[1]+cur];}else if(cur.t==='idOrVar'){var placeable=subPlaceable(locals,args,env,cur.v);return[prev[0],prev[1]+placeable[1]];}},[locals,'']);}
|
||||
function resolveSelector(args,env,expr,index){var selectorName=index[0].v;var selector=resolveIdentifier(args,env,selectorName)[1];if(typeof selector!=='function'){return selector;}
|
||||
var argValue=index[1]?resolveIdentifier(args,env,index[1])[1]:undefined;if(selector===env.__plural){if(argValue===0&&'zero'in expr){return'zero';}
|
||||
if(argValue===1&&'one'in expr){return'one';}
|
||||
if(argValue===2&&'two'in expr){return'two';}}
|
||||
return selector(argValue);}
|
||||
function resolveValue(locals,args,env,expr,index){if(!expr){return[locals,expr];}
|
||||
if(typeof expr==='string'||typeof expr==='boolean'||typeof expr==='number'){return[locals,expr];}
|
||||
if(Array.isArray(expr)){locals.contextIsNonLatin1=expr.some(function($_){return typeof($_)==='string'&&$_.match(nonLatin1);});return interpolate(locals,args,env,expr);}
|
||||
if(index){var selector=resolveSelector(args,env,expr,index);if(expr.hasOwnProperty(selector)){return resolveValue(locals,args,env,expr[selector]);}}
|
||||
if('other'in expr){return resolveValue(locals,args,env,expr.other);}
|
||||
throw new L10nError('Unresolvable value');}
|
||||
var Resolver={createEntry:createEntry,format:format,rePlaceables:rePlaceables};function walkContent(node,fn){if(typeof node==='string'){return fn(node);}
|
||||
if(node.t==='idOrVar'){return node;}
|
||||
var rv=Array.isArray(node)?[]:{};var keys=Object.keys(node);for(var i=0,key;(key=keys[i]);i++){if(key==='$i'||key==='$x'){rv[key]=node[key];}else{rv[key]=walkContent(node[key],fn);}}
|
||||
return rv;}
|
||||
var reAlphas=/[a-zA-Z]/g;var reVowels=/[aeiouAEIOU]/g;var ACCENTED_MAP='\u0226\u0181\u0187\u1E12\u1E16\u0191\u0193\u0126\u012A'+'\u0134\u0136\u013F\u1E3E\u0220\u01FE\u01A4\u024A\u0158'+'\u015E\u0166\u016C\u1E7C\u1E86\u1E8A\u1E8E\u1E90'+'[\\]^_`'+'\u0227\u0180\u0188\u1E13\u1E17\u0192\u0260\u0127\u012B'+'\u0135\u0137\u0140\u1E3F\u019E\u01FF\u01A5\u024B\u0159'+'\u015F\u0167\u016D\u1E7D\u1E87\u1E8B\u1E8F\u1E91';var FLIPPED_MAP='\u2200\u0510\u2183p\u018E\u025F\u05E4HI\u017F'+'\u04FC\u02E5WNO\u0500\xD2\u1D1AS\u22A5\u2229\u0245'+'\uFF2DX\u028EZ'+'[\\]\u1D65_,'+'\u0250q\u0254p\u01DD\u025F\u0183\u0265\u0131\u027E'+'\u029E\u0285\u026Fuodb\u0279s\u0287n\u028C\u028Dx\u028Ez';function makeLonger(val){return val.replace(reVowels,function(match){return match+match.toLowerCase();});}
|
||||
function replaceChars(map,val){return val.replace(reAlphas,function(match){return map.charAt(match.charCodeAt(0)-65);});}
|
||||
var reWords=/[^\W0-9_]+/g;function makeRTL(val){return val.replace(reWords,function(match){return'\u202e'+match+'\u202c';});}
|
||||
var reExcluded=/(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/;function mapContent(fn,val){if(!val){return val;}
|
||||
var parts=val.split(reExcluded);var modified=parts.map(function(part){if(reExcluded.test(part)){return part;}
|
||||
return fn(part);});return modified.join('');}
|
||||
function Pseudo(id,name,charMap,modFn){this.id=id;this.translate=mapContent.bind(null,function(val){return replaceChars(charMap,modFn(val));});this.name=this.translate(name);}
|
||||
var PSEUDO={'fr-x-psaccent':new Pseudo('fr-x-psaccent','Runtime Accented',ACCENTED_MAP,makeLonger),'ar-x-psbidi':new Pseudo('ar-x-psbidi','Runtime Bidi',FLIPPED_MAP,makeRTL)};function Locale(id,ctx){this.id=id;this.ctx=ctx;this.isReady=false;this.entries=Object.create(null);this.entries.__plural=getPluralRule(this.isPseudo()?this.ctx.defaultLocale:id);}
|
||||
Locale.prototype.isPseudo=function(){return this.ctx.qps.indexOf(this.id)!==-1;};var bindingsIO={extra:function(id,ver,path,type,callback,errback){if(type==='properties'){type='text';}
|
||||
navigator.mozApps.getLocalizationResource(id,ver,path,type).then(callback.bind(null,null),errback);},app:function(id,ver,path,type,callback,errback,sync){switch(type){case'properties':io.load(path,callback,sync);break;case'json':io.loadJSON(path,callback,sync);break;}},};Locale.prototype.build=function L_build(callback){var sync=!callback;var ctx=this.ctx;var self=this;var l10nLoads=ctx.resLinks.length;function onL10nLoaded(err){if(err){ctx._emitter.emit('fetcherror',err);}
|
||||
if(--l10nLoads<=0){self.isReady=true;if(callback){callback();}}}
|
||||
if(l10nLoads===0){onL10nLoaded();return;}
|
||||
function onJSONLoaded(err,json){if(!err&&json){self.addAST(json);}
|
||||
onL10nLoaded(err);}
|
||||
function onPropLoaded(err,source){if(!err&&source){var ast=PropertiesParser.parse(ctx,source);self.addAST(ast);}
|
||||
onL10nLoaded(err);}
|
||||
var idToFetch=this.isPseudo()?ctx.defaultLocale:this.id;var appVersion=null;var source='app';if(typeof(navigator)!=='undefined'){source=navigator.mozL10n._config.localeSources[this.id]||'app';appVersion=navigator.mozL10n._config.appVersion;}
|
||||
for(var i=0;i<ctx.resLinks.length;i++){var resLink=decodeURI(ctx.resLinks[i]);var path=resLink.replace('{locale}',idToFetch);var type=path.substr(path.lastIndexOf('.')+1);var cb;switch(type){case'json':cb=onJSONLoaded;break;case'properties':cb=onPropLoaded;break;}
|
||||
bindingsIO[source](this.id,appVersion,path,type,cb,onL10nLoaded,sync);}};function createPseudoEntry(node,entries){return Resolver.createEntry(walkContent(node,PSEUDO[this.id].translate),entries);}
|
||||
Locale.prototype.addAST=function(ast){var createEntry=this.isPseudo()?createPseudoEntry.bind(this):Resolver.createEntry;for(var i=0;i<ast.length;i++){this.entries[ast[i].$i]=createEntry(ast[i],this.entries);}};function Context(id){this.id=id;this.isReady=false;this.isLoading=false;this.defaultLocale='en-US';this.availableLocales=[];this.supportedLocales=[];this.qps=[];this.resLinks=[];this.locales={};this._emitter=new EventEmitter();this._ready=new Promise(this.once.bind(this));}
|
||||
function reportMissing(id,err){this._emitter.emit('notfounderror',err);return id;}
|
||||
function getWithFallback(id){var cur=0;var loc;var locale;while(loc=this.supportedLocales[cur]){locale=this.getLocale(loc);if(!locale.isReady){locale.build(null);}
|
||||
var entry=locale.entries[id];if(entry===undefined){cur++;reportMissing.call(this,id,new L10nError('"'+id+'"'+' not found in '+loc+' in '+this.id,id,loc));continue;}
|
||||
return entry;}
|
||||
throw new L10nError('"'+id+'"'+' missing from all supported locales in '+this.id,id);}
|
||||
function formatTuple(args,entity){try{return Resolver.format(args,entity);}catch(err){this._emitter.emit('resolveerror',err);var locals={error:err};return[locals,entity.id];}}
|
||||
function formatValue(args,entity){if(typeof entity==='string'){return entity;}
|
||||
return formatTuple.call(this,args,entity)[1];}
|
||||
function formatEntity(args,entity){var entityTuple=formatTuple.call(this,args,entity);var value=entityTuple[1];var formatted={value:value,attrs:null,};if(entity.attrs){formatted.attrs=Object.create(null);}
|
||||
for(var key in entity.attrs){var attrTuple=formatTuple.call(this,args,entity.attrs[key]);formatted.attrs[key]=attrTuple[1];}
|
||||
return formatted;}
|
||||
function formatAsync(fn,id,args){return this._ready.then(getWithFallback.bind(this,id)).then(fn.bind(this,args),reportMissing.bind(this,id));}
|
||||
Context.prototype.formatValue=function(id,args){return formatAsync.call(this,formatValue,id,args);};Context.prototype.formatEntity=function(id,args){return formatAsync.call(this,formatEntity,id,args);};function legacyGet(fn,id,args){if(!this.isReady){throw new L10nError('Context not ready');}
|
||||
var entry;try{entry=getWithFallback.call(this,id);}catch(err){if(err.loc){throw err;}
|
||||
reportMissing.call(this,id,err);return'';}
|
||||
return fn.call(this,args,entry);}
|
||||
Context.prototype.get=function(id,args){return legacyGet.call(this,formatValue,id,args);};Context.prototype.getEntity=function(id,args){return legacyGet.call(this,formatEntity,id,args);};Context.prototype.getLocale=function getLocale(code){var locales=this.locales;if(locales[code]){return locales[code];}
|
||||
return locales[code]=new Locale(code,this);};function negotiate(available,requested,defaultLocale){var supportedLocale;for(var i=0;i<requested.length;i++){var locale=requested[i];if(available.indexOf(locale)!==-1){supportedLocale=locale;break;}}
|
||||
if(!supportedLocale||supportedLocale===defaultLocale){return[defaultLocale];}
|
||||
return[supportedLocale,defaultLocale];}
|
||||
function freeze(supported){var locale=this.getLocale(supported[0]);if(locale.isReady){setReady.call(this,supported);}else{locale.build(setReady.bind(this,supported));}}
|
||||
function setReady(supported){this.supportedLocales=supported;this.isReady=true;this._emitter.emit('ready');}
|
||||
Context.prototype.registerLocales=function(defLocale,available){if(defLocale){this.defaultLocale=defLocale;}
|
||||
this.availableLocales=[this.defaultLocale];this.qps=Object.keys(PSEUDO);if(available){for(var i=0,loc;loc=available[i];i++){if(this.availableLocales.indexOf(loc)===-1){this.availableLocales.push(loc);var pos=this.qps.indexOf(loc);if(pos!==-1){this.qps.splice(pos,1);}}}}};Context.prototype.requestLocales=function requestLocales(){if(this.isLoading&&!this.isReady){throw new L10nError('Context not ready');}
|
||||
this.isLoading=true;var requested=Array.prototype.slice.call(arguments);if(requested.length===0){throw new L10nError('No locales requested');}
|
||||
var supported=negotiate(this.availableLocales.concat(this.qps),requested,this.defaultLocale);if(this.supportedLocales[0]!==supported[0]){freeze.call(this,supported);}};Context.prototype.addEventListener=function(type,listener){this._emitter.addEventListener(type,listener);};Context.prototype.removeEventListener=function(type,listener){this._emitter.removeEventListener(type,listener);};Context.prototype.ready=function(callback){if(this.isReady){setTimeout(callback);}
|
||||
this.addEventListener('ready',callback);};Context.prototype.once=function(callback){if(this.isReady){setTimeout(callback);return;}
|
||||
var callAndRemove=(function(){this.removeEventListener('ready',callAndRemove);callback();}).bind(this);this.addEventListener('ready',callAndRemove);};var allowed={elements:['a','em','strong','small','s','cite','q','dfn','abbr','data','time','code','var','samp','kbd','sub','sup','i','b','u','mark','ruby','rt','rp','bdi','bdo','span','br','wbr'],attributes:{global:['title','aria-label','aria-valuetext','aria-moz-hint'],a:['download'],area:['download','alt'],input:['alt','placeholder'],menuitem:['label'],menu:['label'],optgroup:['label'],option:['label'],track:['label'],img:['alt'],textarea:['placeholder'],th:['abbr']}};var rtlList=['ar','he','fa','ps','ar-x-psbidi','ur'];var nodeObserver=null;var pendingElements=null;var moConfig={attributes:true,characterData:false,childList:true,subtree:true,attributeFilter:['data-l10n-id','data-l10n-args']};navigator.mozL10n={ctx:null,get:function get(id,ctxdata){return navigator.mozL10n.ctx.get(id,ctxdata);},formatValue:function(id,ctxdata){return navigator.mozL10n.ctx.formatValue(id,ctxdata);},formatEntity:function(id,ctxdata){return navigator.mozL10n.ctx.formatEntity(id,ctxdata);},translateFragment:function(fragment){return translateFragment.call(navigator.mozL10n,fragment);},setAttributes:setL10nAttributes,getAttributes:getL10nAttributes,ready:function ready(callback){return navigator.mozL10n.ctx.ready(callback);},once:function once(callback){return navigator.mozL10n.ctx.once(callback);},get readyState(){return navigator.mozL10n.ctx.isReady?'complete':'loading';},language:{set code(lang){navigator.mozL10n.ctx.requestLocales(lang);},get code(){return navigator.mozL10n.ctx.supportedLocales[0];},get direction(){return getDirection(navigator.mozL10n.ctx.supportedLocales[0]);}},qps:PSEUDO,_config:{appVersion:null,localeSources:Object.create(null),isPretranslated:false,},_getInternalAPI:function(){return{Error:L10nError,Context:Context,Locale:Locale,Resolver:Resolver,getPluralRule:getPluralRule,rePlaceables:rePlaceables,translateDocument:translateDocument,onMetaInjected:onMetaInjected,PropertiesParser:PropertiesParser,walkContent:walkContent,buildLocaleList:buildLocaleList};}};function getDirection(lang){return(rtlList.indexOf(lang)>=0)?'rtl':'ltr';}
|
||||
var readyStates={loading:0,interactive:1,complete:2};function whenInteractive(callback){if(readyStates[document.readyState]>=readyStates.interactive){callback();return;}
|
||||
document.addEventListener('readystatechange',function l10n_onrsc(){if(readyStates[document.readyState]>=readyStates.interactive){document.removeEventListener('readystatechange',l10n_onrsc);callback();}});}
|
||||
function initObserver(){nodeObserver=new MutationObserver(onMutations.bind(navigator.mozL10n));nodeObserver.observe(document,moConfig);}
|
||||
function init(pretranslate){if(!pretranslate){initObserver();}
|
||||
initResources.call(navigator.mozL10n);}
|
||||
function initResources(){var meta={};var nodes=document.head.querySelectorAll('link[rel="localization"],'+'meta[name="availableLanguages"],'+'meta[name="defaultLanguage"],'+'meta[name="appVersion"],'+'script[type="application/l10n"]');for(var i=0,node;node=nodes[i];i++){var type=node.getAttribute('rel')||node.nodeName.toLowerCase();switch(type){case'localization':this.ctx.resLinks.push(node.getAttribute('href'));break;case'meta':onMetaInjected.call(this,node,meta);break;case'script':onScriptInjected.call(this,node);break;}}
|
||||
var additionalLanguagesPromise;if(navigator.mozApps&&navigator.mozApps.getAdditionalLanguages){additionalLanguagesPromise=navigator.mozApps.getAdditionalLanguages().catch(function(e){console.error('Error while loading getAdditionalLanguages',e);});document.addEventListener('additionallanguageschange',function(evt){registerLocales.call(this,meta,evt.detail);this.ctx.requestLocales.apply(this.ctx,navigator.languages||[navigator.language]);}.bind(this));}else{additionalLanguagesPromise=Promise.resolve();}
|
||||
additionalLanguagesPromise.then(function(extraLangs){registerLocales.call(this,meta,extraLangs);initLocale.call(this);}.bind(this));}
|
||||
function registerLocales(meta,extraLangs){var locales=buildLocaleList.call(this,meta,extraLangs);navigator.mozL10n._config.localeSources=locales[1];this.ctx.registerLocales(locales[0],Object.keys(locales[1]));}
|
||||
function getMatchingLangpack(appVersion,langpacks){for(var i=0,langpack;(langpack=langpacks[i]);i++){if(langpack.target===appVersion){return langpack;}}
|
||||
return null;}
|
||||
function buildLocaleList(meta,extraLangs){var loc,lp;var localeSources=Object.create(null);var defaultLocale=meta.defaultLanguage||this.ctx.defaultLocale;if(meta.availableLanguages){for(loc in meta.availableLanguages){localeSources[loc]='app';}}
|
||||
if(extraLangs){for(loc in extraLangs){lp=getMatchingLangpack(this._config.appVersion,extraLangs[loc]);if(!lp){continue;}
|
||||
if(!(loc in localeSources)||!meta.availableLanguages[loc]||parseInt(lp.revision)>meta.availableLanguages[loc]){localeSources[loc]='extra';}}}
|
||||
if(!(defaultLocale in localeSources)){localeSources[defaultLocale]='app';}
|
||||
return[defaultLocale,localeSources];}
|
||||
function splitAvailableLanguagesString(str){var langs={};str.split(',').forEach(function(lang){lang=lang.trim().split(':');langs[lang[0]]=parseInt(lang[1]);});return langs;}
|
||||
function onMetaInjected(node,meta){switch(node.getAttribute('name')){case'availableLanguages':meta.availableLanguages=splitAvailableLanguagesString(node.getAttribute('content'));break;case'defaultLanguage':meta.defaultLanguage=node.getAttribute('content');break;case'appVersion':navigator.mozL10n._config.appVersion=node.getAttribute('content');break;}}
|
||||
function onScriptInjected(node){var lang=node.getAttribute('lang');var locale=this.ctx.getLocale(lang);locale.addAST(JSON.parse(node.textContent));}
|
||||
function initLocale(){this.ctx.requestLocales.apply(this.ctx,navigator.languages||[navigator.language]);window.addEventListener('languagechange',function l10n_langchange(){this.ctx.requestLocales.apply(this.ctx,navigator.languages||[navigator.language]);}.bind(this));}
|
||||
function localizeMutations(mutations){var mutation;var targets=new Set();for(var i=0;i<mutations.length;i++){mutation=mutations[i];if(mutation.type==='childList'){var addedNode;for(var j=0;j<mutation.addedNodes.length;j++){addedNode=mutation.addedNodes[j];if(addedNode.nodeType!==Node.ELEMENT_NODE){continue;}
|
||||
targets.add(addedNode);}}
|
||||
if(mutation.type==='attributes'){targets.add(mutation.target);}}
|
||||
targets.forEach(function(target){if(target.childElementCount){translateFragment.call(this,target);}else if(target.hasAttribute('data-l10n-id')){translateElement.call(this,target);}},this);}
|
||||
function onMutations(mutations,self){self.disconnect();localizeMutations.call(this,mutations);self.observe(document,moConfig);}
|
||||
function onReady(){if(!navigator.mozL10n._config.isPretranslated){translateDocument.call(this);}
|
||||
navigator.mozL10n._config.isPretranslated=false;if(pendingElements){for(var i=0,element;element=pendingElements[i];i++){translateElement.call(this,element);}
|
||||
pendingElements=null;}
|
||||
if(!nodeObserver){initObserver();}
|
||||
fireLocalizedEvent.call(this);}
|
||||
function fireLocalizedEvent(){var event=new CustomEvent('localized',{'bubbles':false,'cancelable':false,'detail':{'language':this.ctx.supportedLocales[0]}});window.dispatchEvent(event);}
|
||||
var reOverlay=/<|&#?\w+;/;var reHtml=/[&<>]/g;var htmlEntities={'&':'&','<':'<','>':'>',};function translateDocument(){document.documentElement.lang=this.language.code;document.documentElement.dir=this.language.direction;translateFragment.call(this,document.documentElement);}
|
||||
function translateFragment(element){if(typeof element.hasAttribute==='function'&&element.hasAttribute('data-l10n-id')){translateElement.call(this,element);}
|
||||
var nodes=getTranslatableChildren(element);for(var i=0;i<nodes.length;i++){translateElement.call(this,nodes[i]);}}
|
||||
function setL10nAttributes(element,id,args){element.setAttribute('data-l10n-id',id);if(args){element.setAttribute('data-l10n-args',JSON.stringify(args));}}
|
||||
function getL10nAttributes(element){return{id:element.getAttribute('data-l10n-id'),args:JSON.parse(element.getAttribute('data-l10n-args'))};}
|
||||
function getTranslatableChildren(element){return element?element.querySelectorAll('*[data-l10n-id]'):[];}
|
||||
function camelCaseToDashed(string){if(string==='ariaValueText'){return'aria-valuetext';}
|
||||
return string.replace(/[A-Z]/g,function(match){return'-'+match.toLowerCase();}).replace(/^-/,'');}
|
||||
function escapeL10nArgs(match){return htmlEntities[match];}
|
||||
function translateElement(element){if(!this.ctx.isReady){if(!pendingElements){pendingElements=[];}
|
||||
pendingElements.push(element);return;}
|
||||
var l10nId=element.getAttribute('data-l10n-id');if(!l10nId){return false;}
|
||||
var l10nArgs=element.getAttribute('data-l10n-args');var entity=this.ctx.getEntity(l10nId,l10nArgs?JSON.parse(l10nArgs.replace(reHtml,escapeL10nArgs)):undefined);var value=entity.value;if(typeof value==='string'){if(!reOverlay.test(value)){element.textContent=value;}else{var translation=element.ownerDocument.createElement('template');translation.innerHTML=value;overlayElement(element,translation.content);}}
|
||||
for(var key in entity.attrs){var attrName=camelCaseToDashed(key);if(isAttrAllowed({name:attrName},element)){element.setAttribute(attrName,entity.attrs[key]);}}}
|
||||
function overlayElement(sourceElement,translationElement){var result=translationElement.ownerDocument.createDocumentFragment();var k,attr;var childElement;while((childElement=translationElement.childNodes[0])){translationElement.removeChild(childElement);if(childElement.nodeType===Node.TEXT_NODE){result.appendChild(childElement);continue;}
|
||||
var index=getIndexOfType(childElement);var sourceChild=getNthElementOfType(sourceElement,childElement,index);if(sourceChild){overlayElement(sourceChild,childElement);result.appendChild(sourceChild);continue;}
|
||||
if(isElementAllowed(childElement)){var sanitizedChild=childElement.ownerDocument.createElement(childElement.nodeName);overlayElement(sanitizedChild,childElement);result.appendChild(sanitizedChild);continue;}
|
||||
result.appendChild(document.createTextNode(childElement.textContent));}
|
||||
sourceElement.textContent='';sourceElement.appendChild(result);if(translationElement.attributes){for(k=0,attr;(attr=translationElement.attributes[k]);k++){if(isAttrAllowed(attr,sourceElement)){sourceElement.setAttribute(attr.name,attr.value);}}}}
|
||||
function isElementAllowed(element){return allowed.elements.indexOf(element.tagName.toLowerCase())!==-1;}
|
||||
function isAttrAllowed(attr,element){var attrName=attr.name.toLowerCase();var tagName=element.tagName.toLowerCase();if(allowed.attributes.global.indexOf(attrName)!==-1){return true;}
|
||||
if(!allowed.attributes[tagName]){return false;}
|
||||
if(allowed.attributes[tagName].indexOf(attrName)!==-1){return true;}
|
||||
if(tagName==='input'&&attrName==='value'){var type=element.type.toLowerCase();if(type==='submit'||type==='button'||type==='reset'){return true;}}
|
||||
return false;}
|
||||
function getNthElementOfType(context,element,index){var nthOfType=0;for(var i=0,child;child=context.children[i];i++){if(child.nodeType===Node.ELEMENT_NODE&&child.tagName===element.tagName){if(nthOfType===index){return child;}
|
||||
nthOfType++;}}
|
||||
return null;}
|
||||
function getIndexOfType(element){var index=0;var child;while((child=element.previousElementSibling)){if(child.tagName===element.tagName){index++;}}
|
||||
return index;}
|
||||
var DEBUG=false;navigator.mozL10n.ctx=new Context(window.document?document.URL:null);navigator.mozL10n.ctx.ready(onReady.bind(navigator.mozL10n));navigator.mozL10n.ctx.addEventListener('notfounderror',function reportMissingEntity(e){if(DEBUG||e.loc==='en-US'){console.warn(e.toString());}});if(DEBUG){navigator.mozL10n.ctx.addEventListener('fetcherror',console.error.bind(console));navigator.mozL10n.ctx.addEventListener('parseerror',console.error.bind(console));navigator.mozL10n.ctx.addEventListener('resolveerror',console.error.bind(console));}
|
||||
if(window.document){navigator.mozL10n._config.isPretranslated=document.documentElement.lang===navigator.language;var forcePretranslate=!navigator.mozL10n._config.isPretranslated;whenInteractive(init.bind(navigator.mozL10n,forcePretranslate));}
|
||||
document.l10n={setAttributes:navigator.mozL10n.setAttributes,getAttributes:navigator.mozL10n.getAttributes,formatValue:function(id,args){return navigator.mozL10n.formatValue(id,args);},translateFragment:function(frag){return Promise.resolve(navigator.mozL10n.translateFragment(frag));},ready:new Promise(function(resolve){navigator.mozL10n.once(resolve);}),formatValues:function(){var keys=arguments;var resp=keys.map(function(key){if(Array.isArray(key)){return navigator.mozL10n.formatValue(key[0],key[1]);}
|
||||
return navigator.mozL10n.formatValue(key);});return Promise.all(resp);},requestLanguages:function(langs){navigator.mozL10n.ctx.requestLocales.apply(navigator.mozL10n.ctx,langs);},pseudo:{'fr-x-psaccent':{getName:function(){return Promise.resolve(navigator.mozL10n.qps['fr-x-psaccent'].name);},processString:function(s){return Promise.resolve(navigator.mozL10n.qps['fr-x-psaccent'].translate(s));}},'ar-x-psbidi':{getName:function(){return Promise.resolve(navigator.mozL10n.qps['ar-x-psbidi'].name);},processString:function(s){return Promise.resolve(navigator.mozL10n.qps['ar-x-psbidi'].translate(s));}}},};navigator.mozL10n.ready(function(){document.documentElement.setAttribute('langs',navigator.mozL10n.ctx.supportedLocales.join(' '));});navigator.mozL10n.once(function(){window.addEventListener('localized',function(){document.dispatchEvent(new CustomEvent('DOMRetranslated',{bubbles:false,cancelable:false}));});});})(this);
|
||||
10
assets/webapps/ftu.gaiamobile.org/shared/js/language_list.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
;'use strict';(function(exports){var LOCALES_FILE='/shared/resources/languages.json';function readFile(file,callback){return new Promise(function(resolve,reject){var xhr=new XMLHttpRequest();xhr.onreadystatechange=function loadFile(){if(xhr.readyState===4){if(xhr.status===0||xhr.status===200){resolve(xhr.response);}else{reject(xhr.statusText);}}};xhr.open('GET',file,true);xhr.responseType='json';xhr.send();});}
|
||||
function onError(name){console.error('Error checking setting '+name);}
|
||||
function readSetting(name,callback){var settings=window.navigator.mozSettings;if(!settings||!settings.createLock){return callback(null);}
|
||||
return settings.createLock().get(name).then(function(res){return res[name];},onError.bind(null,name));}
|
||||
exports.LanguageList={_languages:null,_readFile:readFile,_readSetting:readSetting,_extendPseudo:function(languages,currentLang,pseudoEnabled){if(!pseudoEnabled){for(var code in languages){var isCurrent=(code===currentLang);if(code.indexOf('-x-ps')>-1&&!isCurrent){delete languages[code];}}}
|
||||
if(!document.l10n){return Promise.resolve(languages);}
|
||||
var runtimePseudoCodes=Object.keys(document.l10n.pseudo).filter(code=>!(code in languages)&&(code===currentLang||pseudoEnabled));function obj(arr1,arr2){const zipped=[];arr1.forEach(x=>{arr2.forEach(y=>{zipped.push([x,y]);});});return zipped.reduce((obj,[x,y])=>Object.assign(obj,{[x]:y}),{});}
|
||||
return Promise.all(runtimePseudoCodes.map(code=>document.l10n.pseudo[code].getName())).then(names=>Object.assign(languages,obj(runtimePseudoCodes,names)));},_extendAdditional:function(languages,ver,additional){for(var lang in additional){for(var i=0,locale;locale=additional[lang][i];i++){if(locale.target===ver){languages[lang]=locale.name;break;}}}},_removeWithNoSpeech:function(languages,srEnabled){if(srEnabled){var speechLangs=new Set(window.speechSynthesis.getVoices().map(v=>v.lang.split('-')[0]));for(var langName in languages){if(!speechLangs.has(langName.split('-')[0])){delete languages[langName];}}}},_copyObj:function(obj){var copy=Object.create(null);for(var prop in obj){copy[prop]=obj[prop];}
|
||||
return copy;},_parseVersion:function(ver){return ver.split('.').slice(0,2).join('.');},_build:function(){return Promise.all([this._languages||(this._languages=this._readFile(LOCALES_FILE)),this._readSetting('language.current'),this._readSetting('devtools.pseudolocalization.enabled')]).then(([langsFromFile,current,pseudoEnabled])=>{var langs=this._copyObj(langsFromFile);return Promise.all([this._extendPseudo(langs,current,pseudoEnabled),this._readSetting('langpack.channel'),current,this._readSetting('accessibility.screenreader'),navigator.mozApps.getAdditionalLanguages()]);}).then(([langs,ver,current,srEnabled,addl])=>{this._extendAdditional(langs,this._parseVersion(ver),addl);this._removeWithNoSpeech(langs,srEnabled);return[langs,current];});},get:function(callback){if(!callback){return;}
|
||||
this._build().then(Function.prototype.apply.bind(callback,null));},wrapBidi:function(langCode,langName){var rtlList=['ar','he','fa','ps','ar-x-psbidi','ur'];var lEmbedBegin=(rtlList.indexOf(langCode)>=0)?'\u202b':'\u202a';var lEmbedEnd='\u202c';return lEmbedBegin+langName+lEmbedEnd;}};}(window));
|
||||
11
assets/webapps/ftu.gaiamobile.org/shared/js/lazy_loader.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
;;;'use strict';var LazyLoader=(function(){function LazyLoader(){this._loaded={};this._isLoading={};}
|
||||
LazyLoader.prototype={_js:function(file,callback){var script=document.createElement('script');script.src=file;script.async=false;script.addEventListener('load',callback);document.head.appendChild(script);this._isLoading[file]=script;},_css:function(file,callback){var style=document.createElement('link');style.type='text/css';style.rel='stylesheet';style.href=file;document.head.appendChild(style);callback();},_html:function(domNode,callback){if(domNode.getAttribute('is')){this.load(['/shared/js/html_imports.js'],function(){HtmlImports.populate(callback);}.bind(this));return;}
|
||||
for(var i=0;i<domNode.childNodes.length;i++){if(domNode.childNodes[i].nodeType==document.COMMENT_NODE){domNode.innerHTML=domNode.childNodes[i].nodeValue;break;}}
|
||||
window.dispatchEvent(new CustomEvent('lazyload',{detail:domNode}));callback();},getJSON:function(file,mozSystem){return new Promise(function(resolve,reject){var xhr;if(mozSystem){xhr=new XMLHttpRequest({mozSystem:true});}else{xhr=new XMLHttpRequest();}
|
||||
xhr.open('GET',file,true);xhr.responseType='json';xhr.onerror=function(error){reject(error);};xhr.onload=function(){if(xhr.response!==null){resolve(xhr.response);}else{reject(new Error('No valid JSON object was found ('+
|
||||
xhr.status+' '+xhr.statusText+')'));}};xhr.send();});},load:function(files,callback){var deferred={};deferred.promise=new Promise(resolve=>{deferred.resolve=resolve;});if(!Array.isArray(files)){files=[files];}
|
||||
var loadsRemaining=files.length,self=this;function perFileCallback(file){if(self._isLoading[file]){delete self._isLoading[file];}
|
||||
self._loaded[file]=true;if(--loadsRemaining===0){deferred.resolve();if(callback){callback();}}}
|
||||
for(var i=0;i<files.length;i++){var file=files[i];if(this._loaded[file.id||file]){perFileCallback(file);}else if(this._isLoading[file]){this._isLoading[file].addEventListener('load',perFileCallback.bind(null,file));}else{var method,idx;if(typeof file==='string'){method=file.match(/\.([^.]+)$/)[1];idx=file;}else{method='html';idx=file.id;}
|
||||
this['_'+method](file,perFileCallback.bind(null,idx));}}
|
||||
return deferred.promise;}};return new LazyLoader();}());
|
||||
135
assets/webapps/ftu.gaiamobile.org/shared/js/mime_mapper.js
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/* exported MimeMapper */
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* MimeMapper helps gaia apps to decide the mapping of mimetype and extension.
|
||||
* The use cases often happen when apps need to know about the exact
|
||||
* mimetypes or extensions, such as to delegate the open web activity, we must
|
||||
* have suitable mimetypes or extensions to request the right activity
|
||||
*
|
||||
* The mapping is basically created according to:
|
||||
* http://en.wikipedia.org/wiki/Internet_media_type
|
||||
*
|
||||
* The supported formats are considered base on the deviceStorage properties:
|
||||
* http://dxr.mozilla.org/mozilla-central/toolkit/content/
|
||||
* devicestorage.properties
|
||||
*
|
||||
*/
|
||||
|
||||
var MimeMapper = {
|
||||
// This list only contains the extensions we currently supported
|
||||
// We should make it more complete for further usages
|
||||
_typeToExtensionMap: {
|
||||
// Image
|
||||
'image/jpeg': 'jpg',
|
||||
'image/png': 'png',
|
||||
'image/gif': 'gif',
|
||||
'image/bmp': 'bmp',
|
||||
// Audio
|
||||
'audio/mpeg': 'mp3',
|
||||
'audio/mp4': 'm4a',
|
||||
'audio/ogg': 'ogg',
|
||||
'audio/webm': 'webm',
|
||||
'audio/3gpp': '3gp',
|
||||
'audio/amr': 'amr',
|
||||
// Video
|
||||
'video/mp4': 'mp4',
|
||||
'video/mpeg': 'mpg',
|
||||
'video/ogg': 'ogg',
|
||||
'video/webm': 'webm',
|
||||
'video/3gpp': '3gp',
|
||||
'video/3gpp2': '3g2',
|
||||
// Application
|
||||
// If we want to support some types, like pdf, just add
|
||||
'application/pdf': 'pdf',
|
||||
'application/vcard': 'vcf',
|
||||
// Text
|
||||
'text/vcard': 'vcf',
|
||||
'text/x-vcard': 'vcf'
|
||||
},
|
||||
|
||||
// This list only contains the mimetypes we currently supported
|
||||
// We should make it more complete for further usages
|
||||
_extensionToTypeMap: {
|
||||
// Image
|
||||
'jpg': 'image/jpeg',
|
||||
'jpeg': 'image/jpeg',
|
||||
'jpe': 'image/jpeg',
|
||||
'png': 'image/png',
|
||||
'gif': 'image/gif',
|
||||
'bmp': 'image/bmp',
|
||||
// Audio
|
||||
'mp3': 'audio/mpeg',
|
||||
'm4a': 'audio/mp4',
|
||||
'm4b': 'audio/mp4',
|
||||
'm4p': 'audio/mp4',
|
||||
'm4r': 'audio/mp4',
|
||||
'aac': 'audio/aac',
|
||||
'opus': 'audio/ogg',
|
||||
'amr': 'audio/amr',
|
||||
// Video
|
||||
'mp4': 'video/mp4',
|
||||
'mpeg': 'video/mpeg',
|
||||
'mpg': 'video/mpeg',
|
||||
'ogv': 'video/ogg',
|
||||
'ogx': 'video/ogg',
|
||||
'webm': 'video/webm',
|
||||
'3gp': 'video/3gpp',
|
||||
'3g2': 'video/3gpp2',
|
||||
'ogg': 'video/ogg',
|
||||
// Application
|
||||
// If we want to support some extensions, like pdf, just add
|
||||
'pdf': 'application/pdf',
|
||||
// Text
|
||||
'vcf': 'text/vcard'
|
||||
},
|
||||
_parseExtension: function(filename) {
|
||||
var array = filename.split('.');
|
||||
return array.length > 1 ? array.pop() : '';
|
||||
},
|
||||
|
||||
isSupportedType: function(mimetype) {
|
||||
return (mimetype in this._typeToExtensionMap);
|
||||
},
|
||||
|
||||
isSupportedExtension: function(extension) {
|
||||
return (extension in this._extensionToTypeMap);
|
||||
},
|
||||
|
||||
isFilenameMatchesType: function(filename, mimetype) {
|
||||
var extension = this._parseExtension(filename);
|
||||
var guessedType = this.guessTypeFromExtension(extension);
|
||||
return (guessedType == mimetype);
|
||||
},
|
||||
|
||||
guessExtensionFromType: function(mimetype) {
|
||||
return this._typeToExtensionMap[mimetype];
|
||||
},
|
||||
|
||||
guessTypeFromExtension: function(extension) {
|
||||
return this._extensionToTypeMap[extension];
|
||||
},
|
||||
|
||||
// If mimetype is not in the supported list, we will try to
|
||||
// predict the possible valid mimetype based on extension.
|
||||
guessTypeFromFileProperties: function(filename, mimetype) {
|
||||
var extension = this._parseExtension(filename);
|
||||
var type = this.isSupportedType(mimetype) ?
|
||||
mimetype : this.guessTypeFromExtension(extension);
|
||||
return type || '';
|
||||
},
|
||||
|
||||
// if mimetype is not supported, preserve the original extension
|
||||
// and add the predict result as new extension.
|
||||
// If both filename and mimetype are not supported, return the original
|
||||
// filename.
|
||||
ensureFilenameMatchesType: function(filename, mimetype) {
|
||||
if (!this.isFilenameMatchesType(filename, mimetype)) {
|
||||
var guessedExt = this.guessExtensionFromType(mimetype);
|
||||
if (guessedExt) {
|
||||
filename += '.' + guessedExt;
|
||||
}
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
;'use strict';var MobileInfo={brazil:{carriers:{'00':'NEXTEL','02':'TIM','03':'TIM','04':'TIM','05':'CLARO','06':'VIVO','07':'CTBC','08':'TIM','10':'VIVO','11':'VIVO','15':'SERCOMTEL','16':'OI','23':'VIVO','24':'OI','31':'OI','32':'CTBC','33':'CTBC','34':'CTBC','37':'AEIOU'},regions:{'11':'SP','12':'SP','13':'SP','14':'SP','15':'SP','16':'SP','17':'SP','18':'SP','19':'SP','21':'RJ','22':'RJ','24':'RJ','27':'ES','28':'ES','31':'MG','32':'MG','33':'MG','34':'MG','35':'MG','37':'MG','38':'MG','41':'PR','42':'PR','43':'PR','44':'PR','45':'PR','46':'PR','47':'SC','48':'SC','49':'SC','51':'RS','53':'RS','54':'RS','55':'RS','61':'DF','62':'GO','63':'TO','64':'GO','65':'MT','66':'MT','67':'MS','68':'AC','69':'RO','71':'BA','73':'BA','74':'BA','75':'BA','77':'BA','79':'SE','81':'PE','82':'AL','83':'PB','84':'RN','85':'CE','86':'PI','87':'PE','88':'CE','89':'PI','91':'PA','92':'AM','93':'PA','94':'PA','95':'RR','96':'AP','97':'AM','98':'MA','99':'MA'}}};var MobileOperator={BRAZIL_MCC:'724',BRAZIL_CELLBROADCAST_CHANNEL:50,userFacingInfo:function mo_userFacingInfo(mobileConnection){var network=mobileConnection.voice.network;var iccid=mobileConnection.iccId;var iccObj=navigator.mozIccManager.getIccById(iccid);var iccInfo=iccObj?iccObj.iccInfo:null;var operator=network?(network.shortName||network.longName):null;if(operator&&iccInfo&&iccInfo.isDisplaySpnRequired&&iccInfo.spn){if(iccInfo.isDisplayNetworkNameRequired&&operator!==iccInfo.spn){operator=operator+' '+iccInfo.spn;}else{operator=iccInfo.spn;}}
|
||||
var carrier,region;if(this.isBrazil(mobileConnection)){var lac=mobileConnection.voice.cell.gsmLocationAreaCode%100;var carriers=MobileInfo.brazil.carriers;var regions=MobileInfo.brazil.regions;carrier=carriers[network.mnc]||(this.BRAZIL_MCC+network.mnc);region=(regions[lac]?regions[lac]+' '+lac:'');}
|
||||
return{'operator':operator,'carrier':carrier,'region':region};},isBrazil:function mo_isBrazil(mobileConnection){var cell=mobileConnection.voice.cell;var net=mobileConnection.voice.network;return net?(net.mcc===this.BRAZIL_MCC&&cell&&cell.gsmLocationAreaCode):null;}};
|
||||
2
assets/webapps/ftu.gaiamobile.org/shared/js/sanitizer.js
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
;(function(root,factory){'use strict';if(typeof define==='function'&&define.amd){define(factory);}else if(typeof exports==='object'){module.exports=factory();}else{root.Sanitizer=factory();}}(this,function(){'use strict';var Sanitizer={_entity:/[&<>"'/]/g,_entities:{'&':'&','<':'<','>':'>','"':'"','\'':''','/':'/'},getEntity:function(s){return Sanitizer._entities[s];},escapeHTML:function(strings,...values){var result='';for(var i=0;i<strings.length;i++){result+=strings[i];if(i<values.length){result+=String(values[i]).replace(Sanitizer._entity,Sanitizer.getEntity);}}
|
||||
return result;},createSafeHTML:function(strings,...values){var escaped=Sanitizer.escapeHTML(strings,...values);return{__html:escaped,toString:function(){return'[object WrappedHTMLObject]';},info:'This is a wrapped HTML object. See https://developer.mozilla.or'+'g/en-US/Firefox_OS/Security/Security_Automation for more.'};},unwrapSafeHTML:function(...htmlObjects){var markupList=htmlObjects.map(function(obj){return obj.__html;});return markupList.join('');}};return Sanitizer;}));
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
;'use strict';var ScreenLayout={defaultQueries:{tiny:'(max-width: 767px)',small:'(min-width: 768px) and (max-width: 991px)',medium:'(min-width: 992px) and (max-width: 1200px)',large:'(min-width: 1201px)',hardwareHomeButton:window.VulpesCompat?'(min-width: 0px)':'(-moz-physical-home-button)'},init:function sl_init(){this.queries=(function(qs){var result={};for(var key in qs){result[key]=window.matchMedia(qs[key]);}
|
||||
return result;})(this.defaultQueries);},_isOnRealDevice:undefined,isOnRealDevice:function sl_isOnRealDevice(){if(typeof(this._isOnRealDevice)!=='undefined'){return this._isOnRealDevice;}
|
||||
if(window.innerWidth===screen.availWidth){this._isOnRealDevice=true;}else{this._isOnRealDevice=false;}
|
||||
return this._isOnRealDevice;},getCurrentLayout:function sl_getCurrentLayout(type){if(type===undefined){for(var name in this.defaultQueries){if(this.queries[name]&&this.queries[name].matches){return name;}}}
|
||||
if(typeof this.queries[type]!=='undefined'){return this.queries[type].matches;}
|
||||
return false;},watch:function sl_watch(name,media){var mediaString=media||this.queries[name].media;if(!mediaString){return;}
|
||||
this.unwatch(name);this.queries[name]=window.matchMedia(mediaString);this.boundHandleChange=this.handleChange.bind(this);this.queries[name].addListener(this.boundHandleChange);},unwatch:function sl_unwatch(name){if(this.queries[name]){this.queries[name].removeListener(this.boundHandleChange);}},handleChange:function sl_handleChange(evt){for(var key in this.queries){if(this.queries[key].media!==evt.media){continue;}
|
||||
window.dispatchEvent(new CustomEvent('screenlayoutchange',{detail:{name:key,status:evt.matches}}));}}};ScreenLayout.init();
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
;'use strict';var SettingsListener={_lock:null,_observers:[],getSettingsLock:function sl_getSettingsLock(){if(this._lock&&!this._lock.closed){return this._lock;}
|
||||
var settings=window.navigator.mozSettings;return(this._lock=settings.createLock());},observe:function sl_observe(name,defaultValue,callback){var settings=window.navigator.mozSettings;if(!settings){window.setTimeout(function(){callback(defaultValue);});return;}
|
||||
var req;try{req=this.getSettingsLock().get(name);}catch(e){console.warn('Stale lock in settings_listener.js.','See https://bugzilla.mozilla.org/show_bug.cgi?id=793239');this._lock=null;req=this.getSettingsLock().get(name);}
|
||||
req.addEventListener('success',(function onsuccess(){callback(typeof(req.result[name])!='undefined'?req.result[name]:defaultValue);}));var settingChanged=function settingChanged(evt){callback(evt.settingValue);};settings.addObserver(name,settingChanged);this._observers.push({name:name,callback:callback,observer:settingChanged});},unobserve:function sl_unobserve(name,callback){var settings=window.navigator.mozSettings;var that=this;this._observers.forEach(function(value,index){if(value.name===name&&value.callback===callback){settings.removeObserver(name,value.observer);that._observers.splice(index,1);}});}};
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
;;'use strict';var Normalizer={initAsciiNormalizer:function normalizer_init(){var equivalentChars={'a':'áăǎâäȧạȁàảȃāąåḁⱥãǽǣæ','A':'ÁĂǍÂÄȦẠȀÀẢȂĀĄÅḀȺÃǼǢÆ','b':'ḃḅɓḇƀƃ','B':'ḂḄƁḆɃƂ','c':'ćčçĉċƈȼ','C':'ĆČÇĈĊƇȻ','d':'ďḑḓḋḍɗḏđƌð','D':'ĎḐḒḊḌƊḎĐƋ','e':'éĕěȩêḙëėẹȅèẻȇēę','E':'ÉĔĚȨÊḘËĖẸȄÈẺȆĒĘ','f':'ḟƒ','F':'ḞƑ','g':'ǵğǧģĝġɠḡǥ','G':'ǴĞǦĢĜĠƓḠǤ','h':'ḫȟḩĥⱨḧḣḥħ','H':'ḪȞḨĤⱧḦḢḤĦ','i':'íĭǐîïịȉìỉȋīįɨĩḭı','I':'ÍĬǏÎÏỊȈÌỈȊĪĮƗĨḬ','j':'ĵɉ','J':'ĴɈ','k':'ḱǩķⱪꝃḳƙḵꝁ','K':'ḰǨĶⱩꝂḲƘḴꝀ','l':'ĺƚľļḽḷⱡꝉḻŀɫł','L':'ĹȽĽĻḼḶⱠꝈḺĿⱢŁ','m':'ḿṁṃɱ','M':'ḾṀṂⱮ','n':'ńňņṋṅṇǹɲṉƞñ','N':'ŃŇŅṊṄṆǸƝṈȠÑ','o':'óŏǒôöȯọőȍòỏơȏꝋꝍōǫøõœ','O':'ÓŎǑÔÖȮỌŐȌÒỎƠȎꝊꝌŌǪØÕŒ','p':'ṕṗꝓƥᵽꝑ','P':'ṔṖꝒƤⱣꝐ','q':'ꝗ','Q':'Ꝗ','r':'ŕřŗṙṛȑȓṟɍɽ','R':'ŔŘŖṘṚȐȒṞɌⱤ','s':'śšşŝșṡṣß','S':'ŚŠŞŜȘṠṢ','t':'ťţṱțⱦṫṭƭṯʈŧ','T':'ŤŢṰȚȾṪṬƬṮƮŦ','u':'úŭǔûṷüṳụűȕùủưȗūųůũṵ','U':'ÚŬǓÛṶÜṲỤŰȔÙỦƯȖŪŲŮŨṴ','v':'ṿʋṽ','V':'ṾƲṼ','w':'ẃŵẅẇẉẁⱳ','W':'ẂŴẄẆẈẀⱲ','x':'ẍẋ','X':'ẌẊ','y':'ýŷÿẏỵỳƴỷỿȳɏỹ','Y':'ÝŶŸẎỴỲƳỶỾȲɎỸ','z':'źžẑⱬżẓȥẕƶ','Z':'ŹŽẐⱫŻẒȤẔƵ'};this._toAsciiForm={};for(var letter in equivalentChars){var accentedForms=equivalentChars[letter];for(var i=accentedForms.length-1;i>=0;i--){this._toAsciiForm[accentedForms[i]]=letter;}}},toAscii:function normalizer_toAscii(str){if(!str||typeof str!='string'){return'';}
|
||||
if(!this._toAsciiForm){Normalizer.initAsciiNormalizer();}
|
||||
var result='';for(var i=0,len=str.length;i<len;i++){result+=this._toAsciiForm[str.charAt(i)]||str.charAt(i);}
|
||||
return result;},escapeHTML:function normalizer_escapeHTML(str,escapeQuotes){if(Array.isArray(str)){return Normalizer.escapeHTML(str.join(' '),escapeQuotes);}
|
||||
if(!str||typeof str!='string'){return'';}
|
||||
var escaped=str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');if(escapeQuotes){return escaped.replace(/"/g,'"').replace(/'/g,''');}
|
||||
return escaped;},unescapeHTML:function normalizer_unescapeHTML(text){return text.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,'\'');},escapeRegExp:function normalizer_escapeRegExp(str){return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,'\\$&');}};
|
||||
25
assets/webapps/ftu.gaiamobile.org/shared/js/tz_select.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
;'use strict';function tzSelect(regionSelector,citySelector,onchange,onload){const TIMEZONE_FILE='/shared/resources/tz.json';const APN_TZ_FILE='/shared/resources/apn_tz.json';const TIMEZONE_NONE='NONE';const TIMEZONE_NETWORK='NETWORK';const TIMEZONE_SIM='SIM';const TIMEZONE_USER='USER';const TIMEZONE_PREVIOUS_SETTING='PREVIOUS_SETTING';function loadJSON(href,callback){var xhr=new XMLHttpRequest();xhr.open('GET',href,true);xhr.responseType='json';xhr.onerror=callback;xhr.onload=function(){callback(xhr.response);};xhr.onerror=function(){console.error('Error getting file');callback(xhr.response);};xhr.send();}
|
||||
function getDefaultTimezoneID(callback){if(!callback){return;}
|
||||
var tzDefault='America/New_York';var source=TIMEZONE_NONE;var mcc,mnc;var connections=window.navigator.mozMobileConnections||[];for(var i=0;i<connections.length;++i){var conn=connections[i];if(conn&&conn.voice&&conn.voice.network&&conn.voice.connected){mcc=conn.voice.network.mcc;mnc=conn.voice.network.mnc;source=TIMEZONE_NETWORK;break;}}
|
||||
if(!mcc&&IccHelper&&IccHelper.iccInfo){mcc=IccHelper.iccInfo.mcc;mnc=IccHelper.iccInfo.mnc;source=TIMEZONE_SIM;if(IccHelper.cardState!=='ready'){IccHelper.addEventListener('iccinfochange',function simReady(){if(IccHelper.iccInfo.mcc){IccHelper.removeEventListener('iccinfochange',simReady);}
|
||||
getDefaultTimezoneID(callback);});}}
|
||||
if(!mcc){callback(tzDefault,TIMEZONE_NONE);return;}
|
||||
loadJSON(APN_TZ_FILE,function(response){if(response){var tz=response[mcc];if(typeof(tz)==='string'){callback(tz,source);return;}else if(tz&&(mnc in tz)){callback(tz[mnc],source);return;}}
|
||||
callback(tzDefault,TIMEZONE_NONE);});}
|
||||
function newTZSelector(onchangeTZ,currentID,source){var gRegion=currentID.replace(/\/.*/,'');var gCity=currentID.replace(/.*?\//,'');var gTZ=null;function fillSelectElement(selector,options){selector.innerHTML='';options.sort(function(a,b){return(a.text>b.text);});for(var i=0;i<options.length;i++){var option=document.createElement('option');option.textContent=options[i].text;option.selected=options[i].selected;option.value=options[i].value;selector.appendChild(option);}}
|
||||
function fillSelectL10nElement(selector,options){selector.innerHTML='';const collator=Intl.Collator(navigator.languages,{usage:'sort',});const frag=document.createDocumentFragment();const items=options.map(option=>{const item=document.createElement('option');item.setAttribute('data-l10n-id',option.l10nId);item.selected=option.selected;item.value=option.value;frag.appendChild(item);return item;});return document.l10n.translateFragment(frag).then(()=>{items.sort((a,b)=>collator.compare(a.textContent,b.textContent));items.forEach(item=>frag.appendChild(item));selector.appendChild(frag);});}
|
||||
function getSelectedText(selector){var options=selector.querySelectorAll('option');return options[selector.selectedIndex].textContent;}
|
||||
function fillRegions(){var options=[];for(var c in gTZ){options.push({l10nId:'tzRegion-'+c,value:c,selected:(c===gRegion)});}
|
||||
fillSelectL10nElement(regionSelector,options).then(fillCities);}
|
||||
function fillCities(){gRegion=regionSelector.value;var list=gTZ[gRegion];var options=[];for(var i=0;i<list.length;i++){options.push({text:list[i].name||list[i].city.replace(/_/g,' '),value:i,selected:(list[i].city===gCity)});}
|
||||
fillSelectElement(citySelector,options);setTimezone();}
|
||||
function setTimezone(){if(source===TIMEZONE_PREVIOUS_SETTING){onload&&onload(getTZInfo(),true);return;}
|
||||
onchangeTZ(getTZInfo(),{'changedByUser':source===TIMEZONE_USER,'needsConfirmation':source!==TIMEZONE_USER&&source!==TIMEZONE_NETWORK});}
|
||||
function getTZInfo(){var res=gTZ[gRegion][citySelector.value];gCity=res.city;var offset=res.offset.split(',');return{id:res.id||gRegion+'/'+res.city,region:getSelectedText(regionSelector),city:getSelectedText(citySelector),cc:res.cc,utcOffset:offset[0],dstOffset:offset[1]};}
|
||||
regionSelector.onchange=function(){source=TIMEZONE_USER;fillCities();};citySelector.onchange=function(){source=TIMEZONE_USER;setTimezone();};loadJSON(TIMEZONE_FILE,function loadTZ(response){gTZ=response;fillRegions();});}
|
||||
function newTZObserver(){var settings=window.navigator.mozSettings;if(!settings){return;}
|
||||
settings.addObserver('time.timezone',function(event){setTimezoneDescription(event.settingValue);});var systemTimeChanged=function(){onchange(systemTimeChanged.tz,systemTimeChanged.needsConfirmation);};function updateTZ(tz,options){systemTimeChanged.tz=tz;systemTimeChanged.needsConfirmation=options.needsConfirmation;var req=settings.createLock().set({'time.timezone':tz.id});req.onsuccess=function updateTZ_callback(){if(options.changedByUser){settings.createLock().set({'time.timezone.user-selected':tz.id});}else{onload&&onload(tz,options.needsConfirmation);}};}
|
||||
function initSelector(initialValue,source){onchange&&window.addEventListener('moztimechange',systemTimeChanged);onchange&&window.addEventListener('unload',function(){window.removeEventListener('moztimechange',systemTimeChanged);});newTZSelector(updateTZ,initialValue,source);}
|
||||
var reqTimezone=settings.createLock().get('time.timezone');reqTimezone.onsuccess=function dt_getStatusSuccess(){var reqUserTZ=settings.createLock().get('time.timezone.user-selected');reqUserTZ.onsuccess=function dt_getUserTimezoneSuccess(){var userSelTimezone=reqUserTZ.result['time.timezone.user-selected'];if(userSelTimezone){initSelector(userSelTimezone,TIMEZONE_PREVIOUS_SETTING);}else{getDefaultTimezoneID(initSelector);}};};function setTimezoneDescription(timezoneID){var oldSelectedIndex=regionSelector.selectedIndex;regionSelector.value=timezoneID.replace(/\/.*/,'');if(regionSelector.selectedIndex==-1){regionSelector.selectedIndex=oldSelectedIndex;}
|
||||
oldSelectedIndex=citySelector.selectedIndex;citySelector.value=timezoneID.replace(/.*?\//,'');if(citySelector.selectedIndex==-1){citySelector.selectedIndex=oldSelectedIndex;}}}
|
||||
newTZObserver();}
|
||||
7
assets/webapps/ftu.gaiamobile.org/shared/js/utilities.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
;'use strict';var DateHelper={todayStarted:function dh_todayStarted(){var now=(new Date()).valueOf();return this.getMidnight(now);},yesterdayStarted:function dh_yesterdayStarted(){var now=(new Date()).valueOf();var dayAgo=now-86400000;return this.getMidnight(dayAgo);},thisWeekStarted:function dh_thisWeekStarted(){var now=new Date();var dayOfTheWeek=now.getDay();var firstDay=new Date(now.getFullYear(),now.getMonth(),now.getDate()-dayOfTheWeek);return this.getMidnight(firstDay);},thisMonthStarted:function dh_thisMonthStarted(){var now=new Date();var firstDay=(new Date(now.getFullYear(),now.getMonth(),1).valueOf());return firstDay;},lastSixMonthsStarted:function dh_lastSixMonthsStarted(){var now=new Date().valueOf();var sixMonthsAgo=now-2629743830*6;return sixMonthsAgo;},thisYearStarted:function dh_thisYearStarted(){var now=new Date();var firstDay=(new Date(now.getFullYear(),0).valueOf());return firstDay;},getMidnight:function dh_getMidnight(timestamp){var day=new Date(timestamp);var midnight=(new Date(day.getFullYear(),day.getMonth(),day.getDate(),0).valueOf());return midnight;}};var NumberHelper={zfill:function nh_zfill(string,len){var s=string;while(s.length<len){s='0'+s;}
|
||||
return s;}};var StringHelper={fromUTF8:function ut_fromUTF8(str){var buf=new Uint8Array(str.length);for(var i=0;i<str.length;i++){buf[i]=str.charCodeAt(i);}
|
||||
return buf;},camelCase:function ut_camelCase(str){var rdashes=/-(.)/g;return str.replace(rdashes,function replacer(str,p1){return p1.toUpperCase();});}};var HtmlHelper={createHighlightHTML:function ut_createHighlightHTML(text,terms){var toHighlight=[];var normalizedText=Normalizer.toAscii(text).toLowerCase();terms.forEach(function(term){term=Normalizer.toAscii(term).toLowerCase();var index=normalizedText.indexOf(term);while(index>=0){for(var i=0,length=term.length;i<length;i++){toHighlight[index+i]=true;}
|
||||
index=normalizedText.indexOf(term,index+term.length);}});var fragment=document.createDocumentFragment();for(var i=0;i<text.length;i++){if(!toHighlight[i]){fragment.appendChild(document.createTextNode(text[i]));}else{var term='';while(toHighlight[i]){term+=text[i];i++;}
|
||||
i--;var mark=document.createElement('mark');mark.textContent=term;fragment.appendChild(mark);}}
|
||||
return fragment;},escapeHTML:function ut_escapeHTML(str,escapeQuotes){var span=document.createElement('span');span.textContent=str;span.innerHTML=span.innerHTML.replace(/\s/g,' ');if(escapeQuotes){return span.innerHTML.replace(/"/g,'"').replace(/'/g,''');}
|
||||
return span.innerHTML;},importElements:function importElements(context){var ids=[].slice.call(arguments,1);ids.forEach(function(id){context[StringHelper.camelCase(id)]=document.getElementById(id);});}};
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
;'use strict';(function(exports){function Version(str){str=str||'0.0';var parts=str.split('.');this.major=parts[0];this.minor=parts[1];this.toString=function(){return''+str;};}
|
||||
function InfoResult(current,previous){this.current=current;this.previous=previous;}
|
||||
InfoResult.prototype.isUpgrade=function(){var prev=this.previous,curr=this.current,isUpgrade=false;if(prev&&curr){isUpgrade=curr.major>prev.major||curr.minor>prev.minor;}
|
||||
return isUpgrade;};InfoResult.prototype.delta=function(){var prev=this.previous,curr=this.current;var delta='';if(prev){delta+=prev.major+'.'+prev.minor;}
|
||||
delta+='..';if(curr){delta+=curr.major+'.'+curr.minor;}
|
||||
return delta;};function getSetting(setting,cb,errback){var req=navigator.mozSettings.createLock().get(setting);req.onsuccess=function(){var value=req.result[setting];if(typeof cb==='function'){cb(value);}};req.onerror=function(){if(typeof errback==='function'){errback(req.error);}};}
|
||||
function getCurrent(){return new Promise(function(resolve,reject){getSetting('deviceinfo.os',function(value){resolve(value?new Version(value):null);},function(err){reject(err);});});}
|
||||
function getPrevious(){return new Promise(function(resolve,reject){getSetting('deviceinfo.previous_os',function(value){resolve(value?new Version(value):null);},function(err){resolve(null);});});}
|
||||
function updatePrevious(str){var currentPromise=str?Promise.resolve(new Version(str)):getCurrent();var setPromise=new Promise(function(resolve,reject){currentPromise.then(function(version){if(version){var req=navigator.mozSettings.createLock().set({'deviceinfo.previous_os':version.toString()});req.onsuccess=function(){resolve(version);};req.onerror=function(){var reason=req.error;reject(reason);};}else{resolve(null);}},function(err){reject('Error getting previous version: ',err);});});return setPromise;}
|
||||
function getVersionInfo(){var info=new InfoResult();var infoPromise=new Promise(function(resolve,reject){Promise.all([getCurrent().then(function(version){info.current=version;}),getPrevious().then(function(version){info.previous=version;})]).then(function(){resolve(info);},reject);});return infoPromise;}
|
||||
exports.VersionHelper={updatePrevious:updatePrevious,getVersionInfo:getVersionInfo};})(window);
|
||||
20
assets/webapps/ftu.gaiamobile.org/shared/js/wifi_helper.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
;'use strict';var WifiHelper={getWifiManager:function(){return this.wifiManager();},wifiManager:function(){return navigator.mozWifiManager;},setPassword:function(network,password,identity,eap,phase2,certificate){var encType=this.getKeyManagement(network);switch(encType){case'WPA-PSK':network.psk=password;break;case'WPA-EAP':network.eap=eap;switch(eap){case'SIM':break;case'PEAP':case'TLS':case'TTLS':if(password&&password.length){network.password=password;}
|
||||
if(identity&&identity.length){network.identity=identity;}
|
||||
if(phase2!='No'){network.phase2=phase2;}
|
||||
if(certificate!='none'){network.serverCertificate=certificate;}
|
||||
break;default:break;}
|
||||
break;case'WEP':network.wep=password;break;default:return;}
|
||||
network.keyManagement=encType;},setSecurity:function(network,encryptions){network.security=encryptions;},getSecurity:function(network){return network.security;},getCapabilities:function(network){return network.capabilities===undefined||network.capabilities===null?[]:network.capabilities;},getKeyManagement:function(network){var key=this.getSecurity(network)[0];if(/WEP$/.test(key)){return'WEP';}
|
||||
if(/PSK$/.test(key)){return'WPA-PSK';}
|
||||
if(/EAP$/.test(key)){return'WPA-EAP';}
|
||||
return'';},isConnected:function(network){return'connected'===this.getNetworkStatus(network);},getNetworkStatus:function(network){var currentConnection=this.getWifiManager().connection;var currentNetwork=currentConnection.network;if(!currentNetwork||!network){return false;}
|
||||
var key=network.ssid+'+'+this.getSecurity(network).join('+');var curkey=currentNetwork.ssid+'+'+
|
||||
this.getSecurity(currentNetwork).join('+');return(key===curkey?currentConnection.status:'disconnected');},isValidInput:function(key,password,identity,eap){function isValidWepKey(password){switch(password.length){case 5:case 13:case 16:case 29:return true;case 10:case 26:case 32:case 58:return!/[^a-fA-F0-9]/.test(password);default:return false;}}
|
||||
switch(key){case'WPA-PSK':if(!password||password.length<8){return false;}
|
||||
break;case'WPA-EAP':switch(eap){case'SIM':break;case'PEAP':case'TLS':case'TTLS':default:if(!password||password.length<1||!identity||identity.length<1){return false;}
|
||||
break;}
|
||||
break;case'WEP':if(!password||!isValidWepKey(password)){return false;}
|
||||
break;}
|
||||
return true;},isSSIDValid:function(ssid){return!!ssid&&ssid.length>0;},isWpsAvailable:function(network){var capabilities=this.getCapabilities(network);for(var i=0;i<capabilities.length;i++){if(/WPS/.test(capabilities[i])){return true;}}
|
||||
return false;},isOpen:function(network){return this.getKeyManagement(network)==='';},isEap:function(network){return this.getKeyManagement(network).indexOf('EAP')!==-1;},_unionOfNetworks:function(available,known){var allNetworks=available||{};var result=[];Object.keys(known).forEach(function(key){if(!allNetworks[key]){allNetworks[key]=known[key];}});Object.keys(allNetworks).forEach(function(key){result.push(allNetworks[key]);});return result;},_networksArrayToObject:function(allNetworks){var self=this;var networksObject={};[].forEach.call(allNetworks,function(network){var key=network.ssid+'+'+
|
||||
self.getSecurity(network).join('+');if(!networksObject[key]||network.relSignalStrength>networksObject[key].relSignalStrength){networksObject[key]=network;}});return networksObject;},_onReqProxySuccess:function(reqProxy,availableNetworks,knownNetworks){reqProxy.result=this._unionOfNetworks(availableNetworks,knownNetworks);reqProxy.onsuccess();},getAvailableAndKnownNetworks:function(){var self=this;var reqProxy={onsuccess:function(){},onerror:function(){}};var knownNetworks={};var availableNetworks={};var knownNetworksReq=null;var availableNetworksReq=this.getWifiManager().getNetworks();availableNetworksReq.onsuccess=function anrOnSuccess(){availableNetworks=self._networksArrayToObject(availableNetworksReq.result);knownNetworksReq=self.getWifiManager().getKnownNetworks();knownNetworksReq.onsuccess=function knrOnSuccess(){knownNetworks=self._networksArrayToObject(knownNetworksReq.result);self._onReqProxySuccess(reqProxy,availableNetworks,knownNetworks);};knownNetworksReq.onerror=function knrOnError(){self._onReqProxySuccess(reqProxy,availableNetworks,knownNetworks);};};availableNetworksReq.onerror=function anrOnError(){reqProxy.error=availableNetworksReq.error;reqProxy.onerror();};return reqProxy;},getSignalLevel:function(network){return Math.min(Math.floor(network.relSignalStrength/20),4);}};
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
|
||||
<title>Facebook</title>
|
||||
|
||||
<!-- Building Blocks -->
|
||||
<link href="/shared/style/headers.css" rel="stylesheet">
|
||||
<link href="/shared/style/confirm.css" rel="stylesheet">
|
||||
<link href="/shared/style/progress_activity.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="/shared/pages/import/style/root.css">
|
||||
<link rel="stylesheet" href="/shared/pages/import/style/curtain.css">
|
||||
|
||||
<meta name="defaultLanguage" content="en-US">
|
||||
<meta name="availableLanguages" content="ar:201601260729, bg:201601260729, bn-BD:201601260729, ca:201601260729, cs:201601260729, da:201601260729, de:201601260729, el:201601260729, en-US:201601260729, es:201601260729, eu:201601260729, fr:201601260729, gl:201601260729, hr:201601260729, hu:201601260729, it:201601260729, ja:201601260729, km:201601260729, ko:201601260729, lt:201601260729, mk:201601260729, ms:201601260729, ne-NP:201601260729, nl:201601260729, pa:201601260729, pl:201601260729, pt-BR:201601260729, ro:201601260729, ru:201601260729, sk:201601260729, sq:201601260729, sr-Cyrl:201601260729, sr-Latn:201601260729, sv-SE:201601260729, tr:201601260729, zh-CN:201601260729, zh-TW:201601260729">
|
||||
<link rel="localization" href="/locales-obj/shared.pages.import.curtain.{locale}.json">
|
||||
<!-- <script type="text/javascript" src="/shared/js/l10n.js"></script> --><script src="./gaia_build_curtain.js" type="text/javascript"></script>
|
||||
<meta content="2.5" name="appVersion"></head>
|
||||
|
||||
<body role="application">
|
||||
<section id="curtain">
|
||||
<form role="dialog" data-type="confirm">
|
||||
|
||||
<!-- Progress activity card -->
|
||||
<section id="progress">
|
||||
<h1 id="progressTitle"></h1>
|
||||
<p role="status">
|
||||
<span id="progressMsg"></span>
|
||||
<progress id="progressElement" class="pack-activity" value="0" max="100"></progress>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- Message card -->
|
||||
<section id="message">
|
||||
<h1 id="messageMsg"></h1>
|
||||
<p role="status">
|
||||
<span></span>
|
||||
<progress class="pack-activity" max="100" value="0"></progress>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- Waiting card -->
|
||||
<section id="wait">
|
||||
<h1 id="waitMsg" data-l10n-id="wait1">Please hold on</h1>
|
||||
<p role="status">
|
||||
<span></span>
|
||||
<progress class="pack-activity" max="100" value="0"></progress>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- Error card -->
|
||||
<section id="error">
|
||||
<h1 id="errorMsg">Error</h1>
|
||||
<p data-l10n-id="error2">Do you want to retry?</p>
|
||||
</section>
|
||||
|
||||
<!-- Timeout card -->
|
||||
<section id="timeout">
|
||||
<h1 id="timeoutMsg">Exceeded timeout</h1>
|
||||
<p data-l10n-id="timeout2">Do you want to retry?</p>
|
||||
</section>
|
||||
|
||||
<!-- Alert card -->
|
||||
<section id="alert">
|
||||
<h1 id="alertMsg"></h1>
|
||||
<p> </p>
|
||||
</section>
|
||||
|
||||
<menu>
|
||||
<button data-l10n-id="curtainCancel" id="cancel">Cancel</button>
|
||||
<button data-l10n-id="curtainRetry" id="retry" class="recommend">Retry</button>
|
||||
<button data-l10n-id="curtainOK" id="ok" class="recommend">OK</button>
|
||||
</menu>
|
||||
</form>
|
||||
|
||||
</section>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,245 @@
|
|||
;(function(window,undefined){'use strict';function L10nError(message,id,loc){this.name='L10nError';this.message=message;this.id=id;this.loc=loc;}
|
||||
L10nError.prototype=Object.create(Error.prototype);L10nError.prototype.constructor=L10nError;var io={_load:function(type,url,callback,sync){var xhr=new XMLHttpRequest();var needParse;if(xhr.overrideMimeType){xhr.overrideMimeType(type);}
|
||||
xhr.open('GET',url,!sync);if(type==='application/json'){if(sync){needParse=true;}else{xhr.responseType='json';}}
|
||||
xhr.addEventListener('load',function io_onload(e){if(e.target.status===200||e.target.status===0){var res=e.target.response||e.target.responseText;callback(null,needParse?JSON.parse(res):res);}else{callback(new L10nError('Not found: '+url));}});xhr.addEventListener('error',callback);xhr.addEventListener('timeout',callback);try{xhr.send(null);}catch(e){if(e.name==='NS_ERROR_FILE_NOT_FOUND'){callback(new L10nError('Not found: '+url));}else{throw e;}}},load:function(url,callback,sync){return io._load('text/plain',url,callback,sync);},loadJSON:function(url,callback,sync){return io._load('application/json',url,callback,sync);}};function EventEmitter(){}
|
||||
EventEmitter.prototype.emit=function ee_emit(){if(!this._listeners){return;}
|
||||
var args=Array.prototype.slice.call(arguments);var type=args.shift();if(!this._listeners[type]){return;}
|
||||
var typeListeners=this._listeners[type].slice();for(var i=0;i<typeListeners.length;i++){typeListeners[i].apply(this,args);}};EventEmitter.prototype.addEventListener=function ee_add(type,listener){if(!this._listeners){this._listeners={};}
|
||||
if(!(type in this._listeners)){this._listeners[type]=[];}
|
||||
this._listeners[type].push(listener);};EventEmitter.prototype.removeEventListener=function ee_rm(type,listener){if(!this._listeners){return;}
|
||||
var typeListeners=this._listeners[type];var pos=typeListeners.indexOf(listener);if(pos===-1){return;}
|
||||
typeListeners.splice(pos,1);};function getPluralRule(lang){var locales2rules={'af':3,'ak':4,'am':4,'ar':1,'asa':3,'az':0,'be':11,'bem':3,'bez':3,'bg':3,'bh':4,'bm':0,'bn':3,'bo':0,'br':20,'brx':3,'bs':11,'ca':3,'cgg':3,'chr':3,'cs':12,'cy':17,'da':3,'de':3,'dv':3,'dz':0,'ee':3,'el':3,'en':3,'eo':3,'es':3,'et':3,'eu':3,'fa':0,'ff':5,'fi':3,'fil':4,'fo':3,'fr':5,'fur':3,'fy':3,'ga':8,'gd':24,'gl':3,'gsw':3,'gu':3,'guw':4,'gv':23,'ha':3,'haw':3,'he':2,'hi':4,'hr':11,'hu':0,'id':0,'ig':0,'ii':0,'is':3,'it':3,'iu':7,'ja':0,'jmc':3,'jv':0,'ka':0,'kab':5,'kaj':3,'kcg':3,'kde':0,'kea':0,'kk':3,'kl':3,'km':0,'kn':0,'ko':0,'ksb':3,'ksh':21,'ku':3,'kw':7,'lag':18,'lb':3,'lg':3,'ln':4,'lo':0,'lt':10,'lv':6,'mas':3,'mg':4,'mk':16,'ml':3,'mn':3,'mo':9,'mr':3,'ms':0,'mt':15,'my':0,'nah':3,'naq':7,'nb':3,'nd':3,'ne':3,'nl':3,'nn':3,'no':3,'nr':3,'nso':4,'ny':3,'nyn':3,'om':3,'or':3,'pa':3,'pap':3,'pl':13,'ps':3,'pt':3,'rm':3,'ro':9,'rof':3,'ru':11,'rwk':3,'sah':0,'saq':3,'se':7,'seh':3,'ses':0,'sg':0,'sh':11,'shi':19,'sk':12,'sl':14,'sma':7,'smi':7,'smj':7,'smn':7,'sms':7,'sn':3,'so':3,'sq':3,'sr':11,'ss':3,'ssy':3,'st':3,'sv':3,'sw':3,'syr':3,'ta':3,'te':3,'teo':3,'th':0,'ti':4,'tig':3,'tk':3,'tl':4,'tn':3,'to':0,'tr':0,'ts':3,'tzm':22,'uk':11,'ur':3,'ve':3,'vi':0,'vun':3,'wa':4,'wae':3,'wo':0,'xh':3,'xog':3,'yo':0,'zh':0,'zu':3};function isIn(n,list){return list.indexOf(n)!==-1;}
|
||||
function isBetween(n,start,end){return typeof n===typeof start&&start<=n&&n<=end;}
|
||||
var pluralRules={'0':function(){return'other';},'1':function(n){if((isBetween((n%100),3,10))){return'few';}
|
||||
if(n===0){return'zero';}
|
||||
if((isBetween((n%100),11,99))){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'2':function(n){if(n!==0&&(n%10)===0){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'3':function(n){if(n===1){return'one';}
|
||||
return'other';},'4':function(n){if((isBetween(n,0,1))){return'one';}
|
||||
return'other';},'5':function(n){if((isBetween(n,0,2))&&n!==2){return'one';}
|
||||
return'other';},'6':function(n){if(n===0){return'zero';}
|
||||
if((n%10)===1&&(n%100)!==11){return'one';}
|
||||
return'other';},'7':function(n){if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'8':function(n){if((isBetween(n,3,6))){return'few';}
|
||||
if((isBetween(n,7,10))){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'9':function(n){if(n===0||n!==1&&(isBetween((n%100),1,19))){return'few';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'10':function(n){if((isBetween((n%10),2,9))&&!(isBetween((n%100),11,19))){return'few';}
|
||||
if((n%10)===1&&!(isBetween((n%100),11,19))){return'one';}
|
||||
return'other';},'11':function(n){if((isBetween((n%10),2,4))&&!(isBetween((n%100),12,14))){return'few';}
|
||||
if((n%10)===0||(isBetween((n%10),5,9))||(isBetween((n%100),11,14))){return'many';}
|
||||
if((n%10)===1&&(n%100)!==11){return'one';}
|
||||
return'other';},'12':function(n){if((isBetween(n,2,4))){return'few';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'13':function(n){if((isBetween((n%10),2,4))&&!(isBetween((n%100),12,14))){return'few';}
|
||||
if(n!==1&&(isBetween((n%10),0,1))||(isBetween((n%10),5,9))||(isBetween((n%100),12,14))){return'many';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'14':function(n){if((isBetween((n%100),3,4))){return'few';}
|
||||
if((n%100)===2){return'two';}
|
||||
if((n%100)===1){return'one';}
|
||||
return'other';},'15':function(n){if(n===0||(isBetween((n%100),2,10))){return'few';}
|
||||
if((isBetween((n%100),11,19))){return'many';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'16':function(n){if((n%10)===1&&n!==11){return'one';}
|
||||
return'other';},'17':function(n){if(n===3){return'few';}
|
||||
if(n===0){return'zero';}
|
||||
if(n===6){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'18':function(n){if(n===0){return'zero';}
|
||||
if((isBetween(n,0,2))&&n!==0&&n!==2){return'one';}
|
||||
return'other';},'19':function(n){if((isBetween(n,2,10))){return'few';}
|
||||
if((isBetween(n,0,1))){return'one';}
|
||||
return'other';},'20':function(n){if((isBetween((n%10),3,4)||((n%10)===9))&&!(isBetween((n%100),10,19)||isBetween((n%100),70,79)||isBetween((n%100),90,99))){return'few';}
|
||||
if((n%1000000)===0&&n!==0){return'many';}
|
||||
if((n%10)===2&&!isIn((n%100),[12,72,92])){return'two';}
|
||||
if((n%10)===1&&!isIn((n%100),[11,71,91])){return'one';}
|
||||
return'other';},'21':function(n){if(n===0){return'zero';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'22':function(n){if((isBetween(n,0,1))||(isBetween(n,11,99))){return'one';}
|
||||
return'other';},'23':function(n){if((isBetween((n%10),1,2))||(n%20)===0){return'one';}
|
||||
return'other';},'24':function(n){if((isBetween(n,3,10)||isBetween(n,13,19))){return'few';}
|
||||
if(isIn(n,[2,12])){return'two';}
|
||||
if(isIn(n,[1,11])){return'one';}
|
||||
return'other';}};var index=locales2rules[lang.replace(/-.*$/,'')];if(!(index in pluralRules)){return function(){return'other';};}
|
||||
return pluralRules[index];}
|
||||
var MAX_PLACEABLES=100;var PropertiesParser={patterns:null,entryIds:null,init:function(){this.patterns={comment:/^\s*#|^\s*$/,entity:/^([^=\s]+)\s*=\s*(.*)$/,multiline:/[^\\]\\$/,index:/\{\[\s*(\w+)(?:\(([^\)]*)\))?\s*\]\}/i,unicode:/\\u([0-9a-fA-F]{1,4})/g,entries:/[^\r\n]+/g,controlChars:/\\([\\\n\r\t\b\f\{\}\"\'])/g,placeables:/\{\{\s*([^\s]*?)\s*\}\}/,};},parse:function(ctx,source){if(!this.patterns){this.init();}
|
||||
var ast=[];this.entryIds=Object.create(null);var entries=source.match(this.patterns.entries);if(!entries){return ast;}
|
||||
for(var i=0;i<entries.length;i++){var line=entries[i];if(this.patterns.comment.test(line)){continue;}
|
||||
while(this.patterns.multiline.test(line)&&i<entries.length){line=line.slice(0,-1)+entries[++i].trim();}
|
||||
var entityMatch=line.match(this.patterns.entity);if(entityMatch){try{this.parseEntity(entityMatch[1],entityMatch[2],ast);}catch(e){if(ctx){ctx._emitter.emit('parseerror',e);}else{throw e;}}}}
|
||||
return ast;},parseEntity:function(id,value,ast){var name,key;var pos=id.indexOf('[');if(pos!==-1){name=id.substr(0,pos);key=id.substring(pos+1,id.length-1);}else{name=id;key=null;}
|
||||
var nameElements=name.split('.');if(nameElements.length>2){throw new L10nError('Error in ID: "'+name+'".'+' Nested attributes are not supported.');}
|
||||
var attr;if(nameElements.length>1){name=nameElements[0];attr=nameElements[1];if(attr[0]==='$'){throw new L10nError('Attribute can\'t start with "$"',id);}}else{attr=null;}
|
||||
this.setEntityValue(name,attr,key,this.unescapeString(value),ast);},setEntityValue:function(id,attr,key,rawValue,ast){var pos,v;var value=rawValue.indexOf('{{')>-1?this.parseString(rawValue):rawValue;if(attr){pos=this.entryIds[id];if(pos===undefined){v={$i:id};if(key){v[attr]={};v[attr][key]=value;}else{v[attr]=value;}
|
||||
ast.push(v);this.entryIds[id]=ast.length-1;return;}
|
||||
if(key){if(typeof(ast[pos][attr])==='string'){ast[pos][attr]={$x:this.parseIndex(ast[pos][attr]),$v:{}};}
|
||||
ast[pos][attr].$v[key]=value;return;}
|
||||
ast[pos][attr]=value;return;}
|
||||
if(key){pos=this.entryIds[id];if(pos===undefined){v={};v[key]=value;ast.push({$i:id,$v:v});this.entryIds[id]=ast.length-1;return;}
|
||||
if(typeof(ast[pos].$v)==='string'){ast[pos].$x=this.parseIndex(ast[pos].$v);ast[pos].$v={};}
|
||||
ast[pos].$v[key]=value;return;}
|
||||
ast.push({$i:id,$v:value});this.entryIds[id]=ast.length-1;},parseString:function(str){var chunks=str.split(this.patterns.placeables);var complexStr=[];var len=chunks.length;var placeablesCount=(len-1)/2;if(placeablesCount>=MAX_PLACEABLES){throw new L10nError('Too many placeables ('+placeablesCount+', max allowed is '+MAX_PLACEABLES+')');}
|
||||
for(var i=0;i<chunks.length;i++){if(chunks[i].length===0){continue;}
|
||||
if(i%2===1){complexStr.push({t:'idOrVar',v:chunks[i]});}else{complexStr.push(chunks[i]);}}
|
||||
return complexStr;},unescapeString:function(str){if(str.lastIndexOf('\\')!==-1){str=str.replace(this.patterns.controlChars,'$1');}
|
||||
return str.replace(this.patterns.unicode,function(match,token){return unescape('%u'+'0000'.slice(token.length)+token);});},parseIndex:function(str){var match=str.match(this.patterns.index);if(!match){throw new L10nError('Malformed index');}
|
||||
if(match[2]){return[{t:'idOrVar',v:match[1]},match[2]];}else{return[{t:'idOrVar',v:match[1]}];}}};var KNOWN_MACROS=['plural'];var MAX_PLACEABLE_LENGTH=2500;var rePlaceables=/\{\{\s*(.+?)\s*\}\}/g;var nonLatin1=/[^\x01-\xFF]/;var FSI='\u2068';var PDI='\u2069';function createEntry(node,env){var keys=Object.keys(node);if(typeof node.$v==='string'&&keys.length===2){return node.$v;}
|
||||
var attrs;for(var i=0,key;key=keys[i];i++){if(key[0]==='$'){continue;}
|
||||
if(!attrs){attrs=Object.create(null);}
|
||||
attrs[key]=createAttribute(node[key],env,node.$i+'.'+key);}
|
||||
return{id:node.$i,value:node.$v!==undefined?node.$v:null,index:node.$x||null,attrs:attrs||null,env:env,dirty:false};}
|
||||
function createAttribute(node,env,id){if(typeof node==='string'){return node;}
|
||||
return{id:id,value:node.$v||(node!==undefined?node:null),index:node.$x||null,env:env,dirty:false};}
|
||||
function format(args,entity){if(typeof entity==='string'){return[{},entity];}
|
||||
if(entity.dirty){throw new L10nError('Cyclic reference detected: '+entity.id);}
|
||||
entity.dirty=true;var rv;try{rv=resolveValue({},args,entity.env,entity.value,entity.index);}finally{entity.dirty=false;}
|
||||
return rv;}
|
||||
function resolveIdentifier(args,env,id){if(KNOWN_MACROS.indexOf(id)>-1){return[{},env['__'+id]];}
|
||||
if(args&&args.hasOwnProperty(id)){if(typeof args[id]==='string'||(typeof args[id]==='number'&&!isNaN(args[id]))){return[{},args[id]];}else{throw new L10nError('Arg must be a string or a number: '+id);}}
|
||||
if(id in env&&id!=='__proto__'){return format(args,env[id]);}
|
||||
throw new L10nError('Unknown reference: '+id);}
|
||||
function subPlaceable(locals,args,env,id){var res;try{res=resolveIdentifier(args,env,id);}catch(err){return[{error:err},'{{ '+id+' }}'];}
|
||||
var value=res[1];if(typeof value==='number'){return res;}
|
||||
if(typeof value==='string'){if(value.length>=MAX_PLACEABLE_LENGTH){throw new L10nError('Too many characters in placeable ('+
|
||||
value.length+', max allowed is '+
|
||||
MAX_PLACEABLE_LENGTH+')');}
|
||||
if(locals.contextIsNonLatin1||value.match(nonLatin1)){res[1]=FSI+value+PDI;}
|
||||
return res;}
|
||||
return[{},'{{ '+id+' }}'];}
|
||||
function interpolate(locals,args,env,arr){return arr.reduce(function(prev,cur){if(typeof cur==='string'){return[prev[0],prev[1]+cur];}else if(cur.t==='idOrVar'){var placeable=subPlaceable(locals,args,env,cur.v);return[prev[0],prev[1]+placeable[1]];}},[locals,'']);}
|
||||
function resolveSelector(args,env,expr,index){var selectorName=index[0].v;var selector=resolveIdentifier(args,env,selectorName)[1];if(typeof selector!=='function'){return selector;}
|
||||
var argValue=index[1]?resolveIdentifier(args,env,index[1])[1]:undefined;if(selector===env.__plural){if(argValue===0&&'zero'in expr){return'zero';}
|
||||
if(argValue===1&&'one'in expr){return'one';}
|
||||
if(argValue===2&&'two'in expr){return'two';}}
|
||||
return selector(argValue);}
|
||||
function resolveValue(locals,args,env,expr,index){if(!expr){return[locals,expr];}
|
||||
if(typeof expr==='string'||typeof expr==='boolean'||typeof expr==='number'){return[locals,expr];}
|
||||
if(Array.isArray(expr)){locals.contextIsNonLatin1=expr.some(function($_){return typeof($_)==='string'&&$_.match(nonLatin1);});return interpolate(locals,args,env,expr);}
|
||||
if(index){var selector=resolveSelector(args,env,expr,index);if(expr.hasOwnProperty(selector)){return resolveValue(locals,args,env,expr[selector]);}}
|
||||
if('other'in expr){return resolveValue(locals,args,env,expr.other);}
|
||||
throw new L10nError('Unresolvable value');}
|
||||
var Resolver={createEntry:createEntry,format:format,rePlaceables:rePlaceables};function walkContent(node,fn){if(typeof node==='string'){return fn(node);}
|
||||
if(node.t==='idOrVar'){return node;}
|
||||
var rv=Array.isArray(node)?[]:{};var keys=Object.keys(node);for(var i=0,key;(key=keys[i]);i++){if(key==='$i'||key==='$x'){rv[key]=node[key];}else{rv[key]=walkContent(node[key],fn);}}
|
||||
return rv;}
|
||||
var reAlphas=/[a-zA-Z]/g;var reVowels=/[aeiouAEIOU]/g;var ACCENTED_MAP='\u0226\u0181\u0187\u1E12\u1E16\u0191\u0193\u0126\u012A'+'\u0134\u0136\u013F\u1E3E\u0220\u01FE\u01A4\u024A\u0158'+'\u015E\u0166\u016C\u1E7C\u1E86\u1E8A\u1E8E\u1E90'+'[\\]^_`'+'\u0227\u0180\u0188\u1E13\u1E17\u0192\u0260\u0127\u012B'+'\u0135\u0137\u0140\u1E3F\u019E\u01FF\u01A5\u024B\u0159'+'\u015F\u0167\u016D\u1E7D\u1E87\u1E8B\u1E8F\u1E91';var FLIPPED_MAP='\u2200\u0510\u2183p\u018E\u025F\u05E4HI\u017F'+'\u04FC\u02E5WNO\u0500\xD2\u1D1AS\u22A5\u2229\u0245'+'\uFF2DX\u028EZ'+'[\\]\u1D65_,'+'\u0250q\u0254p\u01DD\u025F\u0183\u0265\u0131\u027E'+'\u029E\u0285\u026Fuodb\u0279s\u0287n\u028C\u028Dx\u028Ez';function makeLonger(val){return val.replace(reVowels,function(match){return match+match.toLowerCase();});}
|
||||
function replaceChars(map,val){return val.replace(reAlphas,function(match){return map.charAt(match.charCodeAt(0)-65);});}
|
||||
var reWords=/[^\W0-9_]+/g;function makeRTL(val){return val.replace(reWords,function(match){return'\u202e'+match+'\u202c';});}
|
||||
var reExcluded=/(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/;function mapContent(fn,val){if(!val){return val;}
|
||||
var parts=val.split(reExcluded);var modified=parts.map(function(part){if(reExcluded.test(part)){return part;}
|
||||
return fn(part);});return modified.join('');}
|
||||
function Pseudo(id,name,charMap,modFn){this.id=id;this.translate=mapContent.bind(null,function(val){return replaceChars(charMap,modFn(val));});this.name=this.translate(name);}
|
||||
var PSEUDO={'fr-x-psaccent':new Pseudo('fr-x-psaccent','Runtime Accented',ACCENTED_MAP,makeLonger),'ar-x-psbidi':new Pseudo('ar-x-psbidi','Runtime Bidi',FLIPPED_MAP,makeRTL)};function Locale(id,ctx){this.id=id;this.ctx=ctx;this.isReady=false;this.entries=Object.create(null);this.entries.__plural=getPluralRule(this.isPseudo()?this.ctx.defaultLocale:id);}
|
||||
Locale.prototype.isPseudo=function(){return this.ctx.qps.indexOf(this.id)!==-1;};var bindingsIO={extra:function(id,ver,path,type,callback,errback){if(type==='properties'){type='text';}
|
||||
navigator.mozApps.getLocalizationResource(id,ver,path,type).then(callback.bind(null,null),errback);},app:function(id,ver,path,type,callback,errback,sync){switch(type){case'properties':io.load(path,callback,sync);break;case'json':io.loadJSON(path,callback,sync);break;}},};Locale.prototype.build=function L_build(callback){var sync=!callback;var ctx=this.ctx;var self=this;var l10nLoads=ctx.resLinks.length;function onL10nLoaded(err){if(err){ctx._emitter.emit('fetcherror',err);}
|
||||
if(--l10nLoads<=0){self.isReady=true;if(callback){callback();}}}
|
||||
if(l10nLoads===0){onL10nLoaded();return;}
|
||||
function onJSONLoaded(err,json){if(!err&&json){self.addAST(json);}
|
||||
onL10nLoaded(err);}
|
||||
function onPropLoaded(err,source){if(!err&&source){var ast=PropertiesParser.parse(ctx,source);self.addAST(ast);}
|
||||
onL10nLoaded(err);}
|
||||
var idToFetch=this.isPseudo()?ctx.defaultLocale:this.id;var appVersion=null;var source='app';if(typeof(navigator)!=='undefined'){source=navigator.mozL10n._config.localeSources[this.id]||'app';appVersion=navigator.mozL10n._config.appVersion;}
|
||||
for(var i=0;i<ctx.resLinks.length;i++){var resLink=decodeURI(ctx.resLinks[i]);var path=resLink.replace('{locale}',idToFetch);var type=path.substr(path.lastIndexOf('.')+1);var cb;switch(type){case'json':cb=onJSONLoaded;break;case'properties':cb=onPropLoaded;break;}
|
||||
bindingsIO[source](this.id,appVersion,path,type,cb,onL10nLoaded,sync);}};function createPseudoEntry(node,entries){return Resolver.createEntry(walkContent(node,PSEUDO[this.id].translate),entries);}
|
||||
Locale.prototype.addAST=function(ast){var createEntry=this.isPseudo()?createPseudoEntry.bind(this):Resolver.createEntry;for(var i=0;i<ast.length;i++){this.entries[ast[i].$i]=createEntry(ast[i],this.entries);}};function Context(id){this.id=id;this.isReady=false;this.isLoading=false;this.defaultLocale='en-US';this.availableLocales=[];this.supportedLocales=[];this.qps=[];this.resLinks=[];this.locales={};this._emitter=new EventEmitter();this._ready=new Promise(this.once.bind(this));}
|
||||
function reportMissing(id,err){this._emitter.emit('notfounderror',err);return id;}
|
||||
function getWithFallback(id){var cur=0;var loc;var locale;while(loc=this.supportedLocales[cur]){locale=this.getLocale(loc);if(!locale.isReady){locale.build(null);}
|
||||
var entry=locale.entries[id];if(entry===undefined){cur++;reportMissing.call(this,id,new L10nError('"'+id+'"'+' not found in '+loc+' in '+this.id,id,loc));continue;}
|
||||
return entry;}
|
||||
throw new L10nError('"'+id+'"'+' missing from all supported locales in '+this.id,id);}
|
||||
function formatTuple(args,entity){try{return Resolver.format(args,entity);}catch(err){this._emitter.emit('resolveerror',err);var locals={error:err};return[locals,entity.id];}}
|
||||
function formatValue(args,entity){if(typeof entity==='string'){return entity;}
|
||||
return formatTuple.call(this,args,entity)[1];}
|
||||
function formatEntity(args,entity){var entityTuple=formatTuple.call(this,args,entity);var value=entityTuple[1];var formatted={value:value,attrs:null,};if(entity.attrs){formatted.attrs=Object.create(null);}
|
||||
for(var key in entity.attrs){var attrTuple=formatTuple.call(this,args,entity.attrs[key]);formatted.attrs[key]=attrTuple[1];}
|
||||
return formatted;}
|
||||
function formatAsync(fn,id,args){return this._ready.then(getWithFallback.bind(this,id)).then(fn.bind(this,args),reportMissing.bind(this,id));}
|
||||
Context.prototype.formatValue=function(id,args){return formatAsync.call(this,formatValue,id,args);};Context.prototype.formatEntity=function(id,args){return formatAsync.call(this,formatEntity,id,args);};function legacyGet(fn,id,args){if(!this.isReady){throw new L10nError('Context not ready');}
|
||||
var entry;try{entry=getWithFallback.call(this,id);}catch(err){if(err.loc){throw err;}
|
||||
reportMissing.call(this,id,err);return'';}
|
||||
return fn.call(this,args,entry);}
|
||||
Context.prototype.get=function(id,args){return legacyGet.call(this,formatValue,id,args);};Context.prototype.getEntity=function(id,args){return legacyGet.call(this,formatEntity,id,args);};Context.prototype.getLocale=function getLocale(code){var locales=this.locales;if(locales[code]){return locales[code];}
|
||||
return locales[code]=new Locale(code,this);};function negotiate(available,requested,defaultLocale){var supportedLocale;for(var i=0;i<requested.length;i++){var locale=requested[i];if(available.indexOf(locale)!==-1){supportedLocale=locale;break;}}
|
||||
if(!supportedLocale||supportedLocale===defaultLocale){return[defaultLocale];}
|
||||
return[supportedLocale,defaultLocale];}
|
||||
function freeze(supported){var locale=this.getLocale(supported[0]);if(locale.isReady){setReady.call(this,supported);}else{locale.build(setReady.bind(this,supported));}}
|
||||
function setReady(supported){this.supportedLocales=supported;this.isReady=true;this._emitter.emit('ready');}
|
||||
Context.prototype.registerLocales=function(defLocale,available){if(defLocale){this.defaultLocale=defLocale;}
|
||||
this.availableLocales=[this.defaultLocale];this.qps=Object.keys(PSEUDO);if(available){for(var i=0,loc;loc=available[i];i++){if(this.availableLocales.indexOf(loc)===-1){this.availableLocales.push(loc);var pos=this.qps.indexOf(loc);if(pos!==-1){this.qps.splice(pos,1);}}}}};Context.prototype.requestLocales=function requestLocales(){if(this.isLoading&&!this.isReady){throw new L10nError('Context not ready');}
|
||||
this.isLoading=true;var requested=Array.prototype.slice.call(arguments);if(requested.length===0){throw new L10nError('No locales requested');}
|
||||
var supported=negotiate(this.availableLocales.concat(this.qps),requested,this.defaultLocale);if(this.supportedLocales[0]!==supported[0]){freeze.call(this,supported);}};Context.prototype.addEventListener=function(type,listener){this._emitter.addEventListener(type,listener);};Context.prototype.removeEventListener=function(type,listener){this._emitter.removeEventListener(type,listener);};Context.prototype.ready=function(callback){if(this.isReady){setTimeout(callback);}
|
||||
this.addEventListener('ready',callback);};Context.prototype.once=function(callback){if(this.isReady){setTimeout(callback);return;}
|
||||
var callAndRemove=(function(){this.removeEventListener('ready',callAndRemove);callback();}).bind(this);this.addEventListener('ready',callAndRemove);};var allowed={elements:['a','em','strong','small','s','cite','q','dfn','abbr','data','time','code','var','samp','kbd','sub','sup','i','b','u','mark','ruby','rt','rp','bdi','bdo','span','br','wbr'],attributes:{global:['title','aria-label','aria-valuetext','aria-moz-hint'],a:['download'],area:['download','alt'],input:['alt','placeholder'],menuitem:['label'],menu:['label'],optgroup:['label'],option:['label'],track:['label'],img:['alt'],textarea:['placeholder'],th:['abbr']}};var rtlList=['ar','he','fa','ps','ar-x-psbidi','ur'];var nodeObserver=null;var pendingElements=null;var moConfig={attributes:true,characterData:false,childList:true,subtree:true,attributeFilter:['data-l10n-id','data-l10n-args']};navigator.mozL10n={ctx:null,get:function get(id,ctxdata){return navigator.mozL10n.ctx.get(id,ctxdata);},formatValue:function(id,ctxdata){return navigator.mozL10n.ctx.formatValue(id,ctxdata);},formatEntity:function(id,ctxdata){return navigator.mozL10n.ctx.formatEntity(id,ctxdata);},translateFragment:function(fragment){return translateFragment.call(navigator.mozL10n,fragment);},setAttributes:setL10nAttributes,getAttributes:getL10nAttributes,ready:function ready(callback){return navigator.mozL10n.ctx.ready(callback);},once:function once(callback){return navigator.mozL10n.ctx.once(callback);},get readyState(){return navigator.mozL10n.ctx.isReady?'complete':'loading';},language:{set code(lang){navigator.mozL10n.ctx.requestLocales(lang);},get code(){return navigator.mozL10n.ctx.supportedLocales[0];},get direction(){return getDirection(navigator.mozL10n.ctx.supportedLocales[0]);}},qps:PSEUDO,_config:{appVersion:null,localeSources:Object.create(null),isPretranslated:false,},_getInternalAPI:function(){return{Error:L10nError,Context:Context,Locale:Locale,Resolver:Resolver,getPluralRule:getPluralRule,rePlaceables:rePlaceables,translateDocument:translateDocument,onMetaInjected:onMetaInjected,PropertiesParser:PropertiesParser,walkContent:walkContent,buildLocaleList:buildLocaleList};}};function getDirection(lang){return(rtlList.indexOf(lang)>=0)?'rtl':'ltr';}
|
||||
var readyStates={loading:0,interactive:1,complete:2};function whenInteractive(callback){if(readyStates[document.readyState]>=readyStates.interactive){callback();return;}
|
||||
document.addEventListener('readystatechange',function l10n_onrsc(){if(readyStates[document.readyState]>=readyStates.interactive){document.removeEventListener('readystatechange',l10n_onrsc);callback();}});}
|
||||
function initObserver(){nodeObserver=new MutationObserver(onMutations.bind(navigator.mozL10n));nodeObserver.observe(document,moConfig);}
|
||||
function init(pretranslate){if(!pretranslate){initObserver();}
|
||||
initResources.call(navigator.mozL10n);}
|
||||
function initResources(){var meta={};var nodes=document.head.querySelectorAll('link[rel="localization"],'+'meta[name="availableLanguages"],'+'meta[name="defaultLanguage"],'+'meta[name="appVersion"],'+'script[type="application/l10n"]');for(var i=0,node;node=nodes[i];i++){var type=node.getAttribute('rel')||node.nodeName.toLowerCase();switch(type){case'localization':this.ctx.resLinks.push(node.getAttribute('href'));break;case'meta':onMetaInjected.call(this,node,meta);break;case'script':onScriptInjected.call(this,node);break;}}
|
||||
var additionalLanguagesPromise;if(navigator.mozApps&&navigator.mozApps.getAdditionalLanguages){additionalLanguagesPromise=navigator.mozApps.getAdditionalLanguages().catch(function(e){console.error('Error while loading getAdditionalLanguages',e);});document.addEventListener('additionallanguageschange',function(evt){registerLocales.call(this,meta,evt.detail);this.ctx.requestLocales.apply(this.ctx,navigator.languages||[navigator.language]);}.bind(this));}else{additionalLanguagesPromise=Promise.resolve();}
|
||||
additionalLanguagesPromise.then(function(extraLangs){registerLocales.call(this,meta,extraLangs);initLocale.call(this);}.bind(this));}
|
||||
function registerLocales(meta,extraLangs){var locales=buildLocaleList.call(this,meta,extraLangs);navigator.mozL10n._config.localeSources=locales[1];this.ctx.registerLocales(locales[0],Object.keys(locales[1]));}
|
||||
function getMatchingLangpack(appVersion,langpacks){for(var i=0,langpack;(langpack=langpacks[i]);i++){if(langpack.target===appVersion){return langpack;}}
|
||||
return null;}
|
||||
function buildLocaleList(meta,extraLangs){var loc,lp;var localeSources=Object.create(null);var defaultLocale=meta.defaultLanguage||this.ctx.defaultLocale;if(meta.availableLanguages){for(loc in meta.availableLanguages){localeSources[loc]='app';}}
|
||||
if(extraLangs){for(loc in extraLangs){lp=getMatchingLangpack(this._config.appVersion,extraLangs[loc]);if(!lp){continue;}
|
||||
if(!(loc in localeSources)||!meta.availableLanguages[loc]||parseInt(lp.revision)>meta.availableLanguages[loc]){localeSources[loc]='extra';}}}
|
||||
if(!(defaultLocale in localeSources)){localeSources[defaultLocale]='app';}
|
||||
return[defaultLocale,localeSources];}
|
||||
function splitAvailableLanguagesString(str){var langs={};str.split(',').forEach(function(lang){lang=lang.trim().split(':');langs[lang[0]]=parseInt(lang[1]);});return langs;}
|
||||
function onMetaInjected(node,meta){switch(node.getAttribute('name')){case'availableLanguages':meta.availableLanguages=splitAvailableLanguagesString(node.getAttribute('content'));break;case'defaultLanguage':meta.defaultLanguage=node.getAttribute('content');break;case'appVersion':navigator.mozL10n._config.appVersion=node.getAttribute('content');break;}}
|
||||
function onScriptInjected(node){var lang=node.getAttribute('lang');var locale=this.ctx.getLocale(lang);locale.addAST(JSON.parse(node.textContent));}
|
||||
function initLocale(){this.ctx.requestLocales.apply(this.ctx,navigator.languages||[navigator.language]);window.addEventListener('languagechange',function l10n_langchange(){this.ctx.requestLocales.apply(this.ctx,navigator.languages||[navigator.language]);}.bind(this));}
|
||||
function localizeMutations(mutations){var mutation;var targets=new Set();for(var i=0;i<mutations.length;i++){mutation=mutations[i];if(mutation.type==='childList'){var addedNode;for(var j=0;j<mutation.addedNodes.length;j++){addedNode=mutation.addedNodes[j];if(addedNode.nodeType!==Node.ELEMENT_NODE){continue;}
|
||||
targets.add(addedNode);}}
|
||||
if(mutation.type==='attributes'){targets.add(mutation.target);}}
|
||||
targets.forEach(function(target){if(target.childElementCount){translateFragment.call(this,target);}else if(target.hasAttribute('data-l10n-id')){translateElement.call(this,target);}},this);}
|
||||
function onMutations(mutations,self){self.disconnect();localizeMutations.call(this,mutations);self.observe(document,moConfig);}
|
||||
function onReady(){if(!navigator.mozL10n._config.isPretranslated){translateDocument.call(this);}
|
||||
navigator.mozL10n._config.isPretranslated=false;if(pendingElements){for(var i=0,element;element=pendingElements[i];i++){translateElement.call(this,element);}
|
||||
pendingElements=null;}
|
||||
if(!nodeObserver){initObserver();}
|
||||
fireLocalizedEvent.call(this);}
|
||||
function fireLocalizedEvent(){var event=new CustomEvent('localized',{'bubbles':false,'cancelable':false,'detail':{'language':this.ctx.supportedLocales[0]}});window.dispatchEvent(event);}
|
||||
var reOverlay=/<|&#?\w+;/;var reHtml=/[&<>]/g;var htmlEntities={'&':'&','<':'<','>':'>',};function translateDocument(){document.documentElement.lang=this.language.code;document.documentElement.dir=this.language.direction;translateFragment.call(this,document.documentElement);}
|
||||
function translateFragment(element){if(typeof element.hasAttribute==='function'&&element.hasAttribute('data-l10n-id')){translateElement.call(this,element);}
|
||||
var nodes=getTranslatableChildren(element);for(var i=0;i<nodes.length;i++){translateElement.call(this,nodes[i]);}}
|
||||
function setL10nAttributes(element,id,args){element.setAttribute('data-l10n-id',id);if(args){element.setAttribute('data-l10n-args',JSON.stringify(args));}}
|
||||
function getL10nAttributes(element){return{id:element.getAttribute('data-l10n-id'),args:JSON.parse(element.getAttribute('data-l10n-args'))};}
|
||||
function getTranslatableChildren(element){return element?element.querySelectorAll('*[data-l10n-id]'):[];}
|
||||
function camelCaseToDashed(string){if(string==='ariaValueText'){return'aria-valuetext';}
|
||||
return string.replace(/[A-Z]/g,function(match){return'-'+match.toLowerCase();}).replace(/^-/,'');}
|
||||
function escapeL10nArgs(match){return htmlEntities[match];}
|
||||
function translateElement(element){if(!this.ctx.isReady){if(!pendingElements){pendingElements=[];}
|
||||
pendingElements.push(element);return;}
|
||||
var l10nId=element.getAttribute('data-l10n-id');if(!l10nId){return false;}
|
||||
var l10nArgs=element.getAttribute('data-l10n-args');var entity=this.ctx.getEntity(l10nId,l10nArgs?JSON.parse(l10nArgs.replace(reHtml,escapeL10nArgs)):undefined);var value=entity.value;if(typeof value==='string'){if(!reOverlay.test(value)){element.textContent=value;}else{var translation=element.ownerDocument.createElement('template');translation.innerHTML=value;overlayElement(element,translation.content);}}
|
||||
for(var key in entity.attrs){var attrName=camelCaseToDashed(key);if(isAttrAllowed({name:attrName},element)){element.setAttribute(attrName,entity.attrs[key]);}}}
|
||||
function overlayElement(sourceElement,translationElement){var result=translationElement.ownerDocument.createDocumentFragment();var k,attr;var childElement;while((childElement=translationElement.childNodes[0])){translationElement.removeChild(childElement);if(childElement.nodeType===Node.TEXT_NODE){result.appendChild(childElement);continue;}
|
||||
var index=getIndexOfType(childElement);var sourceChild=getNthElementOfType(sourceElement,childElement,index);if(sourceChild){overlayElement(sourceChild,childElement);result.appendChild(sourceChild);continue;}
|
||||
if(isElementAllowed(childElement)){var sanitizedChild=childElement.ownerDocument.createElement(childElement.nodeName);overlayElement(sanitizedChild,childElement);result.appendChild(sanitizedChild);continue;}
|
||||
result.appendChild(document.createTextNode(childElement.textContent));}
|
||||
sourceElement.textContent='';sourceElement.appendChild(result);if(translationElement.attributes){for(k=0,attr;(attr=translationElement.attributes[k]);k++){if(isAttrAllowed(attr,sourceElement)){sourceElement.setAttribute(attr.name,attr.value);}}}}
|
||||
function isElementAllowed(element){return allowed.elements.indexOf(element.tagName.toLowerCase())!==-1;}
|
||||
function isAttrAllowed(attr,element){var attrName=attr.name.toLowerCase();var tagName=element.tagName.toLowerCase();if(allowed.attributes.global.indexOf(attrName)!==-1){return true;}
|
||||
if(!allowed.attributes[tagName]){return false;}
|
||||
if(allowed.attributes[tagName].indexOf(attrName)!==-1){return true;}
|
||||
if(tagName==='input'&&attrName==='value'){var type=element.type.toLowerCase();if(type==='submit'||type==='button'||type==='reset'){return true;}}
|
||||
return false;}
|
||||
function getNthElementOfType(context,element,index){var nthOfType=0;for(var i=0,child;child=context.children[i];i++){if(child.nodeType===Node.ELEMENT_NODE&&child.tagName===element.tagName){if(nthOfType===index){return child;}
|
||||
nthOfType++;}}
|
||||
return null;}
|
||||
function getIndexOfType(element){var index=0;var child;while((child=element.previousElementSibling)){if(child.tagName===element.tagName){index++;}}
|
||||
return index;}
|
||||
var DEBUG=false;navigator.mozL10n.ctx=new Context(window.document?document.URL:null);navigator.mozL10n.ctx.ready(onReady.bind(navigator.mozL10n));navigator.mozL10n.ctx.addEventListener('notfounderror',function reportMissingEntity(e){if(DEBUG||e.loc==='en-US'){console.warn(e.toString());}});if(DEBUG){navigator.mozL10n.ctx.addEventListener('fetcherror',console.error.bind(console));navigator.mozL10n.ctx.addEventListener('parseerror',console.error.bind(console));navigator.mozL10n.ctx.addEventListener('resolveerror',console.error.bind(console));}
|
||||
if(window.document){navigator.mozL10n._config.isPretranslated=document.documentElement.lang===navigator.language;var forcePretranslate=!navigator.mozL10n._config.isPretranslated;whenInteractive(init.bind(navigator.mozL10n,forcePretranslate));}
|
||||
document.l10n={setAttributes:navigator.mozL10n.setAttributes,getAttributes:navigator.mozL10n.getAttributes,formatValue:function(id,args){return navigator.mozL10n.formatValue(id,args);},translateFragment:function(frag){return Promise.resolve(navigator.mozL10n.translateFragment(frag));},ready:new Promise(function(resolve){navigator.mozL10n.once(resolve);}),formatValues:function(){var keys=arguments;var resp=keys.map(function(key){if(Array.isArray(key)){return navigator.mozL10n.formatValue(key[0],key[1]);}
|
||||
return navigator.mozL10n.formatValue(key);});return Promise.all(resp);},requestLanguages:function(langs){navigator.mozL10n.ctx.requestLocales.apply(navigator.mozL10n.ctx,langs);},pseudo:{'fr-x-psaccent':{getName:function(){return Promise.resolve(navigator.mozL10n.qps['fr-x-psaccent'].name);},processString:function(s){return Promise.resolve(navigator.mozL10n.qps['fr-x-psaccent'].translate(s));}},'ar-x-psbidi':{getName:function(){return Promise.resolve(navigator.mozL10n.qps['ar-x-psbidi'].name);},processString:function(s){return Promise.resolve(navigator.mozL10n.qps['ar-x-psbidi'].translate(s));}}},};navigator.mozL10n.ready(function(){document.documentElement.setAttribute('langs',navigator.mozL10n.ctx.supportedLocales.join(' '));});navigator.mozL10n.once(function(){window.addEventListener('localized',function(){document.dispatchEvent(new CustomEvent('DOMRetranslated',{bubbles:false,cancelable:false}));});});})(this);
|
||||
|
|
@ -0,0 +1,290 @@
|
|||
;;;(function(window,undefined){'use strict';function L10nError(message,id,loc){this.name='L10nError';this.message=message;this.id=id;this.loc=loc;}
|
||||
L10nError.prototype=Object.create(Error.prototype);L10nError.prototype.constructor=L10nError;var io={_load:function(type,url,callback,sync){var xhr=new XMLHttpRequest();var needParse;if(xhr.overrideMimeType){xhr.overrideMimeType(type);}
|
||||
xhr.open('GET',url,!sync);if(type==='application/json'){if(sync){needParse=true;}else{xhr.responseType='json';}}
|
||||
xhr.addEventListener('load',function io_onload(e){if(e.target.status===200||e.target.status===0){var res=e.target.response||e.target.responseText;callback(null,needParse?JSON.parse(res):res);}else{callback(new L10nError('Not found: '+url));}});xhr.addEventListener('error',callback);xhr.addEventListener('timeout',callback);try{xhr.send(null);}catch(e){if(e.name==='NS_ERROR_FILE_NOT_FOUND'){callback(new L10nError('Not found: '+url));}else{throw e;}}},load:function(url,callback,sync){return io._load('text/plain',url,callback,sync);},loadJSON:function(url,callback,sync){return io._load('application/json',url,callback,sync);}};function EventEmitter(){}
|
||||
EventEmitter.prototype.emit=function ee_emit(){if(!this._listeners){return;}
|
||||
var args=Array.prototype.slice.call(arguments);var type=args.shift();if(!this._listeners[type]){return;}
|
||||
var typeListeners=this._listeners[type].slice();for(var i=0;i<typeListeners.length;i++){typeListeners[i].apply(this,args);}};EventEmitter.prototype.addEventListener=function ee_add(type,listener){if(!this._listeners){this._listeners={};}
|
||||
if(!(type in this._listeners)){this._listeners[type]=[];}
|
||||
this._listeners[type].push(listener);};EventEmitter.prototype.removeEventListener=function ee_rm(type,listener){if(!this._listeners){return;}
|
||||
var typeListeners=this._listeners[type];var pos=typeListeners.indexOf(listener);if(pos===-1){return;}
|
||||
typeListeners.splice(pos,1);};function getPluralRule(lang){var locales2rules={'af':3,'ak':4,'am':4,'ar':1,'asa':3,'az':0,'be':11,'bem':3,'bez':3,'bg':3,'bh':4,'bm':0,'bn':3,'bo':0,'br':20,'brx':3,'bs':11,'ca':3,'cgg':3,'chr':3,'cs':12,'cy':17,'da':3,'de':3,'dv':3,'dz':0,'ee':3,'el':3,'en':3,'eo':3,'es':3,'et':3,'eu':3,'fa':0,'ff':5,'fi':3,'fil':4,'fo':3,'fr':5,'fur':3,'fy':3,'ga':8,'gd':24,'gl':3,'gsw':3,'gu':3,'guw':4,'gv':23,'ha':3,'haw':3,'he':2,'hi':4,'hr':11,'hu':0,'id':0,'ig':0,'ii':0,'is':3,'it':3,'iu':7,'ja':0,'jmc':3,'jv':0,'ka':0,'kab':5,'kaj':3,'kcg':3,'kde':0,'kea':0,'kk':3,'kl':3,'km':0,'kn':0,'ko':0,'ksb':3,'ksh':21,'ku':3,'kw':7,'lag':18,'lb':3,'lg':3,'ln':4,'lo':0,'lt':10,'lv':6,'mas':3,'mg':4,'mk':16,'ml':3,'mn':3,'mo':9,'mr':3,'ms':0,'mt':15,'my':0,'nah':3,'naq':7,'nb':3,'nd':3,'ne':3,'nl':3,'nn':3,'no':3,'nr':3,'nso':4,'ny':3,'nyn':3,'om':3,'or':3,'pa':3,'pap':3,'pl':13,'ps':3,'pt':3,'rm':3,'ro':9,'rof':3,'ru':11,'rwk':3,'sah':0,'saq':3,'se':7,'seh':3,'ses':0,'sg':0,'sh':11,'shi':19,'sk':12,'sl':14,'sma':7,'smi':7,'smj':7,'smn':7,'sms':7,'sn':3,'so':3,'sq':3,'sr':11,'ss':3,'ssy':3,'st':3,'sv':3,'sw':3,'syr':3,'ta':3,'te':3,'teo':3,'th':0,'ti':4,'tig':3,'tk':3,'tl':4,'tn':3,'to':0,'tr':0,'ts':3,'tzm':22,'uk':11,'ur':3,'ve':3,'vi':0,'vun':3,'wa':4,'wae':3,'wo':0,'xh':3,'xog':3,'yo':0,'zh':0,'zu':3};function isIn(n,list){return list.indexOf(n)!==-1;}
|
||||
function isBetween(n,start,end){return typeof n===typeof start&&start<=n&&n<=end;}
|
||||
var pluralRules={'0':function(){return'other';},'1':function(n){if((isBetween((n%100),3,10))){return'few';}
|
||||
if(n===0){return'zero';}
|
||||
if((isBetween((n%100),11,99))){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'2':function(n){if(n!==0&&(n%10)===0){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'3':function(n){if(n===1){return'one';}
|
||||
return'other';},'4':function(n){if((isBetween(n,0,1))){return'one';}
|
||||
return'other';},'5':function(n){if((isBetween(n,0,2))&&n!==2){return'one';}
|
||||
return'other';},'6':function(n){if(n===0){return'zero';}
|
||||
if((n%10)===1&&(n%100)!==11){return'one';}
|
||||
return'other';},'7':function(n){if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'8':function(n){if((isBetween(n,3,6))){return'few';}
|
||||
if((isBetween(n,7,10))){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'9':function(n){if(n===0||n!==1&&(isBetween((n%100),1,19))){return'few';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'10':function(n){if((isBetween((n%10),2,9))&&!(isBetween((n%100),11,19))){return'few';}
|
||||
if((n%10)===1&&!(isBetween((n%100),11,19))){return'one';}
|
||||
return'other';},'11':function(n){if((isBetween((n%10),2,4))&&!(isBetween((n%100),12,14))){return'few';}
|
||||
if((n%10)===0||(isBetween((n%10),5,9))||(isBetween((n%100),11,14))){return'many';}
|
||||
if((n%10)===1&&(n%100)!==11){return'one';}
|
||||
return'other';},'12':function(n){if((isBetween(n,2,4))){return'few';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'13':function(n){if((isBetween((n%10),2,4))&&!(isBetween((n%100),12,14))){return'few';}
|
||||
if(n!==1&&(isBetween((n%10),0,1))||(isBetween((n%10),5,9))||(isBetween((n%100),12,14))){return'many';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'14':function(n){if((isBetween((n%100),3,4))){return'few';}
|
||||
if((n%100)===2){return'two';}
|
||||
if((n%100)===1){return'one';}
|
||||
return'other';},'15':function(n){if(n===0||(isBetween((n%100),2,10))){return'few';}
|
||||
if((isBetween((n%100),11,19))){return'many';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'16':function(n){if((n%10)===1&&n!==11){return'one';}
|
||||
return'other';},'17':function(n){if(n===3){return'few';}
|
||||
if(n===0){return'zero';}
|
||||
if(n===6){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'18':function(n){if(n===0){return'zero';}
|
||||
if((isBetween(n,0,2))&&n!==0&&n!==2){return'one';}
|
||||
return'other';},'19':function(n){if((isBetween(n,2,10))){return'few';}
|
||||
if((isBetween(n,0,1))){return'one';}
|
||||
return'other';},'20':function(n){if((isBetween((n%10),3,4)||((n%10)===9))&&!(isBetween((n%100),10,19)||isBetween((n%100),70,79)||isBetween((n%100),90,99))){return'few';}
|
||||
if((n%1000000)===0&&n!==0){return'many';}
|
||||
if((n%10)===2&&!isIn((n%100),[12,72,92])){return'two';}
|
||||
if((n%10)===1&&!isIn((n%100),[11,71,91])){return'one';}
|
||||
return'other';},'21':function(n){if(n===0){return'zero';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'22':function(n){if((isBetween(n,0,1))||(isBetween(n,11,99))){return'one';}
|
||||
return'other';},'23':function(n){if((isBetween((n%10),1,2))||(n%20)===0){return'one';}
|
||||
return'other';},'24':function(n){if((isBetween(n,3,10)||isBetween(n,13,19))){return'few';}
|
||||
if(isIn(n,[2,12])){return'two';}
|
||||
if(isIn(n,[1,11])){return'one';}
|
||||
return'other';}};var index=locales2rules[lang.replace(/-.*$/,'')];if(!(index in pluralRules)){return function(){return'other';};}
|
||||
return pluralRules[index];}
|
||||
var MAX_PLACEABLES=100;var PropertiesParser={patterns:null,entryIds:null,init:function(){this.patterns={comment:/^\s*#|^\s*$/,entity:/^([^=\s]+)\s*=\s*(.*)$/,multiline:/[^\\]\\$/,index:/\{\[\s*(\w+)(?:\(([^\)]*)\))?\s*\]\}/i,unicode:/\\u([0-9a-fA-F]{1,4})/g,entries:/[^\r\n]+/g,controlChars:/\\([\\\n\r\t\b\f\{\}\"\'])/g,placeables:/\{\{\s*([^\s]*?)\s*\}\}/,};},parse:function(ctx,source){if(!this.patterns){this.init();}
|
||||
var ast=[];this.entryIds=Object.create(null);var entries=source.match(this.patterns.entries);if(!entries){return ast;}
|
||||
for(var i=0;i<entries.length;i++){var line=entries[i];if(this.patterns.comment.test(line)){continue;}
|
||||
while(this.patterns.multiline.test(line)&&i<entries.length){line=line.slice(0,-1)+entries[++i].trim();}
|
||||
var entityMatch=line.match(this.patterns.entity);if(entityMatch){try{this.parseEntity(entityMatch[1],entityMatch[2],ast);}catch(e){if(ctx){ctx._emitter.emit('parseerror',e);}else{throw e;}}}}
|
||||
return ast;},parseEntity:function(id,value,ast){var name,key;var pos=id.indexOf('[');if(pos!==-1){name=id.substr(0,pos);key=id.substring(pos+1,id.length-1);}else{name=id;key=null;}
|
||||
var nameElements=name.split('.');if(nameElements.length>2){throw new L10nError('Error in ID: "'+name+'".'+' Nested attributes are not supported.');}
|
||||
var attr;if(nameElements.length>1){name=nameElements[0];attr=nameElements[1];if(attr[0]==='$'){throw new L10nError('Attribute can\'t start with "$"',id);}}else{attr=null;}
|
||||
this.setEntityValue(name,attr,key,this.unescapeString(value),ast);},setEntityValue:function(id,attr,key,rawValue,ast){var pos,v;var value=rawValue.indexOf('{{')>-1?this.parseString(rawValue):rawValue;if(attr){pos=this.entryIds[id];if(pos===undefined){v={$i:id};if(key){v[attr]={};v[attr][key]=value;}else{v[attr]=value;}
|
||||
ast.push(v);this.entryIds[id]=ast.length-1;return;}
|
||||
if(key){if(typeof(ast[pos][attr])==='string'){ast[pos][attr]={$x:this.parseIndex(ast[pos][attr]),$v:{}};}
|
||||
ast[pos][attr].$v[key]=value;return;}
|
||||
ast[pos][attr]=value;return;}
|
||||
if(key){pos=this.entryIds[id];if(pos===undefined){v={};v[key]=value;ast.push({$i:id,$v:v});this.entryIds[id]=ast.length-1;return;}
|
||||
if(typeof(ast[pos].$v)==='string'){ast[pos].$x=this.parseIndex(ast[pos].$v);ast[pos].$v={};}
|
||||
ast[pos].$v[key]=value;return;}
|
||||
ast.push({$i:id,$v:value});this.entryIds[id]=ast.length-1;},parseString:function(str){var chunks=str.split(this.patterns.placeables);var complexStr=[];var len=chunks.length;var placeablesCount=(len-1)/2;if(placeablesCount>=MAX_PLACEABLES){throw new L10nError('Too many placeables ('+placeablesCount+', max allowed is '+MAX_PLACEABLES+')');}
|
||||
for(var i=0;i<chunks.length;i++){if(chunks[i].length===0){continue;}
|
||||
if(i%2===1){complexStr.push({t:'idOrVar',v:chunks[i]});}else{complexStr.push(chunks[i]);}}
|
||||
return complexStr;},unescapeString:function(str){if(str.lastIndexOf('\\')!==-1){str=str.replace(this.patterns.controlChars,'$1');}
|
||||
return str.replace(this.patterns.unicode,function(match,token){return unescape('%u'+'0000'.slice(token.length)+token);});},parseIndex:function(str){var match=str.match(this.patterns.index);if(!match){throw new L10nError('Malformed index');}
|
||||
if(match[2]){return[{t:'idOrVar',v:match[1]},match[2]];}else{return[{t:'idOrVar',v:match[1]}];}}};var KNOWN_MACROS=['plural'];var MAX_PLACEABLE_LENGTH=2500;var rePlaceables=/\{\{\s*(.+?)\s*\}\}/g;var nonLatin1=/[^\x01-\xFF]/;var FSI='\u2068';var PDI='\u2069';function createEntry(node,env){var keys=Object.keys(node);if(typeof node.$v==='string'&&keys.length===2){return node.$v;}
|
||||
var attrs;for(var i=0,key;key=keys[i];i++){if(key[0]==='$'){continue;}
|
||||
if(!attrs){attrs=Object.create(null);}
|
||||
attrs[key]=createAttribute(node[key],env,node.$i+'.'+key);}
|
||||
return{id:node.$i,value:node.$v!==undefined?node.$v:null,index:node.$x||null,attrs:attrs||null,env:env,dirty:false};}
|
||||
function createAttribute(node,env,id){if(typeof node==='string'){return node;}
|
||||
return{id:id,value:node.$v||(node!==undefined?node:null),index:node.$x||null,env:env,dirty:false};}
|
||||
function format(args,entity){if(typeof entity==='string'){return[{},entity];}
|
||||
if(entity.dirty){throw new L10nError('Cyclic reference detected: '+entity.id);}
|
||||
entity.dirty=true;var rv;try{rv=resolveValue({},args,entity.env,entity.value,entity.index);}finally{entity.dirty=false;}
|
||||
return rv;}
|
||||
function resolveIdentifier(args,env,id){if(KNOWN_MACROS.indexOf(id)>-1){return[{},env['__'+id]];}
|
||||
if(args&&args.hasOwnProperty(id)){if(typeof args[id]==='string'||(typeof args[id]==='number'&&!isNaN(args[id]))){return[{},args[id]];}else{throw new L10nError('Arg must be a string or a number: '+id);}}
|
||||
if(id in env&&id!=='__proto__'){return format(args,env[id]);}
|
||||
throw new L10nError('Unknown reference: '+id);}
|
||||
function subPlaceable(locals,args,env,id){var res;try{res=resolveIdentifier(args,env,id);}catch(err){return[{error:err},'{{ '+id+' }}'];}
|
||||
var value=res[1];if(typeof value==='number'){return res;}
|
||||
if(typeof value==='string'){if(value.length>=MAX_PLACEABLE_LENGTH){throw new L10nError('Too many characters in placeable ('+
|
||||
value.length+', max allowed is '+
|
||||
MAX_PLACEABLE_LENGTH+')');}
|
||||
if(locals.contextIsNonLatin1||value.match(nonLatin1)){res[1]=FSI+value+PDI;}
|
||||
return res;}
|
||||
return[{},'{{ '+id+' }}'];}
|
||||
function interpolate(locals,args,env,arr){return arr.reduce(function(prev,cur){if(typeof cur==='string'){return[prev[0],prev[1]+cur];}else if(cur.t==='idOrVar'){var placeable=subPlaceable(locals,args,env,cur.v);return[prev[0],prev[1]+placeable[1]];}},[locals,'']);}
|
||||
function resolveSelector(args,env,expr,index){var selectorName=index[0].v;var selector=resolveIdentifier(args,env,selectorName)[1];if(typeof selector!=='function'){return selector;}
|
||||
var argValue=index[1]?resolveIdentifier(args,env,index[1])[1]:undefined;if(selector===env.__plural){if(argValue===0&&'zero'in expr){return'zero';}
|
||||
if(argValue===1&&'one'in expr){return'one';}
|
||||
if(argValue===2&&'two'in expr){return'two';}}
|
||||
return selector(argValue);}
|
||||
function resolveValue(locals,args,env,expr,index){if(!expr){return[locals,expr];}
|
||||
if(typeof expr==='string'||typeof expr==='boolean'||typeof expr==='number'){return[locals,expr];}
|
||||
if(Array.isArray(expr)){locals.contextIsNonLatin1=expr.some(function($_){return typeof($_)==='string'&&$_.match(nonLatin1);});return interpolate(locals,args,env,expr);}
|
||||
if(index){var selector=resolveSelector(args,env,expr,index);if(expr.hasOwnProperty(selector)){return resolveValue(locals,args,env,expr[selector]);}}
|
||||
if('other'in expr){return resolveValue(locals,args,env,expr.other);}
|
||||
throw new L10nError('Unresolvable value');}
|
||||
var Resolver={createEntry:createEntry,format:format,rePlaceables:rePlaceables};function walkContent(node,fn){if(typeof node==='string'){return fn(node);}
|
||||
if(node.t==='idOrVar'){return node;}
|
||||
var rv=Array.isArray(node)?[]:{};var keys=Object.keys(node);for(var i=0,key;(key=keys[i]);i++){if(key==='$i'||key==='$x'){rv[key]=node[key];}else{rv[key]=walkContent(node[key],fn);}}
|
||||
return rv;}
|
||||
var reAlphas=/[a-zA-Z]/g;var reVowels=/[aeiouAEIOU]/g;var ACCENTED_MAP='\u0226\u0181\u0187\u1E12\u1E16\u0191\u0193\u0126\u012A'+'\u0134\u0136\u013F\u1E3E\u0220\u01FE\u01A4\u024A\u0158'+'\u015E\u0166\u016C\u1E7C\u1E86\u1E8A\u1E8E\u1E90'+'[\\]^_`'+'\u0227\u0180\u0188\u1E13\u1E17\u0192\u0260\u0127\u012B'+'\u0135\u0137\u0140\u1E3F\u019E\u01FF\u01A5\u024B\u0159'+'\u015F\u0167\u016D\u1E7D\u1E87\u1E8B\u1E8F\u1E91';var FLIPPED_MAP='\u2200\u0510\u2183p\u018E\u025F\u05E4HI\u017F'+'\u04FC\u02E5WNO\u0500\xD2\u1D1AS\u22A5\u2229\u0245'+'\uFF2DX\u028EZ'+'[\\]\u1D65_,'+'\u0250q\u0254p\u01DD\u025F\u0183\u0265\u0131\u027E'+'\u029E\u0285\u026Fuodb\u0279s\u0287n\u028C\u028Dx\u028Ez';function makeLonger(val){return val.replace(reVowels,function(match){return match+match.toLowerCase();});}
|
||||
function replaceChars(map,val){return val.replace(reAlphas,function(match){return map.charAt(match.charCodeAt(0)-65);});}
|
||||
var reWords=/[^\W0-9_]+/g;function makeRTL(val){return val.replace(reWords,function(match){return'\u202e'+match+'\u202c';});}
|
||||
var reExcluded=/(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/;function mapContent(fn,val){if(!val){return val;}
|
||||
var parts=val.split(reExcluded);var modified=parts.map(function(part){if(reExcluded.test(part)){return part;}
|
||||
return fn(part);});return modified.join('');}
|
||||
function Pseudo(id,name,charMap,modFn){this.id=id;this.translate=mapContent.bind(null,function(val){return replaceChars(charMap,modFn(val));});this.name=this.translate(name);}
|
||||
var PSEUDO={'fr-x-psaccent':new Pseudo('fr-x-psaccent','Runtime Accented',ACCENTED_MAP,makeLonger),'ar-x-psbidi':new Pseudo('ar-x-psbidi','Runtime Bidi',FLIPPED_MAP,makeRTL)};function Locale(id,ctx){this.id=id;this.ctx=ctx;this.isReady=false;this.entries=Object.create(null);this.entries.__plural=getPluralRule(this.isPseudo()?this.ctx.defaultLocale:id);}
|
||||
Locale.prototype.isPseudo=function(){return this.ctx.qps.indexOf(this.id)!==-1;};var bindingsIO={extra:function(id,ver,path,type,callback,errback){if(type==='properties'){type='text';}
|
||||
navigator.mozApps.getLocalizationResource(id,ver,path,type).then(callback.bind(null,null),errback);},app:function(id,ver,path,type,callback,errback,sync){switch(type){case'properties':io.load(path,callback,sync);break;case'json':io.loadJSON(path,callback,sync);break;}},};Locale.prototype.build=function L_build(callback){var sync=!callback;var ctx=this.ctx;var self=this;var l10nLoads=ctx.resLinks.length;function onL10nLoaded(err){if(err){ctx._emitter.emit('fetcherror',err);}
|
||||
if(--l10nLoads<=0){self.isReady=true;if(callback){callback();}}}
|
||||
if(l10nLoads===0){onL10nLoaded();return;}
|
||||
function onJSONLoaded(err,json){if(!err&&json){self.addAST(json);}
|
||||
onL10nLoaded(err);}
|
||||
function onPropLoaded(err,source){if(!err&&source){var ast=PropertiesParser.parse(ctx,source);self.addAST(ast);}
|
||||
onL10nLoaded(err);}
|
||||
var idToFetch=this.isPseudo()?ctx.defaultLocale:this.id;var appVersion=null;var source='app';if(typeof(navigator)!=='undefined'){source=navigator.mozL10n._config.localeSources[this.id]||'app';appVersion=navigator.mozL10n._config.appVersion;}
|
||||
for(var i=0;i<ctx.resLinks.length;i++){var resLink=decodeURI(ctx.resLinks[i]);var path=resLink.replace('{locale}',idToFetch);var type=path.substr(path.lastIndexOf('.')+1);var cb;switch(type){case'json':cb=onJSONLoaded;break;case'properties':cb=onPropLoaded;break;}
|
||||
bindingsIO[source](this.id,appVersion,path,type,cb,onL10nLoaded,sync);}};function createPseudoEntry(node,entries){return Resolver.createEntry(walkContent(node,PSEUDO[this.id].translate),entries);}
|
||||
Locale.prototype.addAST=function(ast){var createEntry=this.isPseudo()?createPseudoEntry.bind(this):Resolver.createEntry;for(var i=0;i<ast.length;i++){this.entries[ast[i].$i]=createEntry(ast[i],this.entries);}};function Context(id){this.id=id;this.isReady=false;this.isLoading=false;this.defaultLocale='en-US';this.availableLocales=[];this.supportedLocales=[];this.qps=[];this.resLinks=[];this.locales={};this._emitter=new EventEmitter();this._ready=new Promise(this.once.bind(this));}
|
||||
function reportMissing(id,err){this._emitter.emit('notfounderror',err);return id;}
|
||||
function getWithFallback(id){var cur=0;var loc;var locale;while(loc=this.supportedLocales[cur]){locale=this.getLocale(loc);if(!locale.isReady){locale.build(null);}
|
||||
var entry=locale.entries[id];if(entry===undefined){cur++;reportMissing.call(this,id,new L10nError('"'+id+'"'+' not found in '+loc+' in '+this.id,id,loc));continue;}
|
||||
return entry;}
|
||||
throw new L10nError('"'+id+'"'+' missing from all supported locales in '+this.id,id);}
|
||||
function formatTuple(args,entity){try{return Resolver.format(args,entity);}catch(err){this._emitter.emit('resolveerror',err);var locals={error:err};return[locals,entity.id];}}
|
||||
function formatValue(args,entity){if(typeof entity==='string'){return entity;}
|
||||
return formatTuple.call(this,args,entity)[1];}
|
||||
function formatEntity(args,entity){var entityTuple=formatTuple.call(this,args,entity);var value=entityTuple[1];var formatted={value:value,attrs:null,};if(entity.attrs){formatted.attrs=Object.create(null);}
|
||||
for(var key in entity.attrs){var attrTuple=formatTuple.call(this,args,entity.attrs[key]);formatted.attrs[key]=attrTuple[1];}
|
||||
return formatted;}
|
||||
function formatAsync(fn,id,args){return this._ready.then(getWithFallback.bind(this,id)).then(fn.bind(this,args),reportMissing.bind(this,id));}
|
||||
Context.prototype.formatValue=function(id,args){return formatAsync.call(this,formatValue,id,args);};Context.prototype.formatEntity=function(id,args){return formatAsync.call(this,formatEntity,id,args);};function legacyGet(fn,id,args){if(!this.isReady){throw new L10nError('Context not ready');}
|
||||
var entry;try{entry=getWithFallback.call(this,id);}catch(err){if(err.loc){throw err;}
|
||||
reportMissing.call(this,id,err);return'';}
|
||||
return fn.call(this,args,entry);}
|
||||
Context.prototype.get=function(id,args){return legacyGet.call(this,formatValue,id,args);};Context.prototype.getEntity=function(id,args){return legacyGet.call(this,formatEntity,id,args);};Context.prototype.getLocale=function getLocale(code){var locales=this.locales;if(locales[code]){return locales[code];}
|
||||
return locales[code]=new Locale(code,this);};function negotiate(available,requested,defaultLocale){var supportedLocale;for(var i=0;i<requested.length;i++){var locale=requested[i];if(available.indexOf(locale)!==-1){supportedLocale=locale;break;}}
|
||||
if(!supportedLocale||supportedLocale===defaultLocale){return[defaultLocale];}
|
||||
return[supportedLocale,defaultLocale];}
|
||||
function freeze(supported){var locale=this.getLocale(supported[0]);if(locale.isReady){setReady.call(this,supported);}else{locale.build(setReady.bind(this,supported));}}
|
||||
function setReady(supported){this.supportedLocales=supported;this.isReady=true;this._emitter.emit('ready');}
|
||||
Context.prototype.registerLocales=function(defLocale,available){if(defLocale){this.defaultLocale=defLocale;}
|
||||
this.availableLocales=[this.defaultLocale];this.qps=Object.keys(PSEUDO);if(available){for(var i=0,loc;loc=available[i];i++){if(this.availableLocales.indexOf(loc)===-1){this.availableLocales.push(loc);var pos=this.qps.indexOf(loc);if(pos!==-1){this.qps.splice(pos,1);}}}}};Context.prototype.requestLocales=function requestLocales(){if(this.isLoading&&!this.isReady){throw new L10nError('Context not ready');}
|
||||
this.isLoading=true;var requested=Array.prototype.slice.call(arguments);if(requested.length===0){throw new L10nError('No locales requested');}
|
||||
var supported=negotiate(this.availableLocales.concat(this.qps),requested,this.defaultLocale);if(this.supportedLocales[0]!==supported[0]){freeze.call(this,supported);}};Context.prototype.addEventListener=function(type,listener){this._emitter.addEventListener(type,listener);};Context.prototype.removeEventListener=function(type,listener){this._emitter.removeEventListener(type,listener);};Context.prototype.ready=function(callback){if(this.isReady){setTimeout(callback);}
|
||||
this.addEventListener('ready',callback);};Context.prototype.once=function(callback){if(this.isReady){setTimeout(callback);return;}
|
||||
var callAndRemove=(function(){this.removeEventListener('ready',callAndRemove);callback();}).bind(this);this.addEventListener('ready',callAndRemove);};var allowed={elements:['a','em','strong','small','s','cite','q','dfn','abbr','data','time','code','var','samp','kbd','sub','sup','i','b','u','mark','ruby','rt','rp','bdi','bdo','span','br','wbr'],attributes:{global:['title','aria-label','aria-valuetext','aria-moz-hint'],a:['download'],area:['download','alt'],input:['alt','placeholder'],menuitem:['label'],menu:['label'],optgroup:['label'],option:['label'],track:['label'],img:['alt'],textarea:['placeholder'],th:['abbr']}};var rtlList=['ar','he','fa','ps','ar-x-psbidi','ur'];var nodeObserver=null;var pendingElements=null;var moConfig={attributes:true,characterData:false,childList:true,subtree:true,attributeFilter:['data-l10n-id','data-l10n-args']};navigator.mozL10n={ctx:null,get:function get(id,ctxdata){return navigator.mozL10n.ctx.get(id,ctxdata);},formatValue:function(id,ctxdata){return navigator.mozL10n.ctx.formatValue(id,ctxdata);},formatEntity:function(id,ctxdata){return navigator.mozL10n.ctx.formatEntity(id,ctxdata);},translateFragment:function(fragment){return translateFragment.call(navigator.mozL10n,fragment);},setAttributes:setL10nAttributes,getAttributes:getL10nAttributes,ready:function ready(callback){return navigator.mozL10n.ctx.ready(callback);},once:function once(callback){return navigator.mozL10n.ctx.once(callback);},get readyState(){return navigator.mozL10n.ctx.isReady?'complete':'loading';},language:{set code(lang){navigator.mozL10n.ctx.requestLocales(lang);},get code(){return navigator.mozL10n.ctx.supportedLocales[0];},get direction(){return getDirection(navigator.mozL10n.ctx.supportedLocales[0]);}},qps:PSEUDO,_config:{appVersion:null,localeSources:Object.create(null),isPretranslated:false,},_getInternalAPI:function(){return{Error:L10nError,Context:Context,Locale:Locale,Resolver:Resolver,getPluralRule:getPluralRule,rePlaceables:rePlaceables,translateDocument:translateDocument,onMetaInjected:onMetaInjected,PropertiesParser:PropertiesParser,walkContent:walkContent,buildLocaleList:buildLocaleList};}};function getDirection(lang){return(rtlList.indexOf(lang)>=0)?'rtl':'ltr';}
|
||||
var readyStates={loading:0,interactive:1,complete:2};function whenInteractive(callback){if(readyStates[document.readyState]>=readyStates.interactive){callback();return;}
|
||||
document.addEventListener('readystatechange',function l10n_onrsc(){if(readyStates[document.readyState]>=readyStates.interactive){document.removeEventListener('readystatechange',l10n_onrsc);callback();}});}
|
||||
function initObserver(){nodeObserver=new MutationObserver(onMutations.bind(navigator.mozL10n));nodeObserver.observe(document,moConfig);}
|
||||
function init(pretranslate){if(!pretranslate){initObserver();}
|
||||
initResources.call(navigator.mozL10n);}
|
||||
function initResources(){var meta={};var nodes=document.head.querySelectorAll('link[rel="localization"],'+'meta[name="availableLanguages"],'+'meta[name="defaultLanguage"],'+'meta[name="appVersion"],'+'script[type="application/l10n"]');for(var i=0,node;node=nodes[i];i++){var type=node.getAttribute('rel')||node.nodeName.toLowerCase();switch(type){case'localization':this.ctx.resLinks.push(node.getAttribute('href'));break;case'meta':onMetaInjected.call(this,node,meta);break;case'script':onScriptInjected.call(this,node);break;}}
|
||||
var additionalLanguagesPromise;if(navigator.mozApps&&navigator.mozApps.getAdditionalLanguages){additionalLanguagesPromise=navigator.mozApps.getAdditionalLanguages().catch(function(e){console.error('Error while loading getAdditionalLanguages',e);});document.addEventListener('additionallanguageschange',function(evt){registerLocales.call(this,meta,evt.detail);this.ctx.requestLocales.apply(this.ctx,navigator.languages||[navigator.language]);}.bind(this));}else{additionalLanguagesPromise=Promise.resolve();}
|
||||
additionalLanguagesPromise.then(function(extraLangs){registerLocales.call(this,meta,extraLangs);initLocale.call(this);}.bind(this));}
|
||||
function registerLocales(meta,extraLangs){var locales=buildLocaleList.call(this,meta,extraLangs);navigator.mozL10n._config.localeSources=locales[1];this.ctx.registerLocales(locales[0],Object.keys(locales[1]));}
|
||||
function getMatchingLangpack(appVersion,langpacks){for(var i=0,langpack;(langpack=langpacks[i]);i++){if(langpack.target===appVersion){return langpack;}}
|
||||
return null;}
|
||||
function buildLocaleList(meta,extraLangs){var loc,lp;var localeSources=Object.create(null);var defaultLocale=meta.defaultLanguage||this.ctx.defaultLocale;if(meta.availableLanguages){for(loc in meta.availableLanguages){localeSources[loc]='app';}}
|
||||
if(extraLangs){for(loc in extraLangs){lp=getMatchingLangpack(this._config.appVersion,extraLangs[loc]);if(!lp){continue;}
|
||||
if(!(loc in localeSources)||!meta.availableLanguages[loc]||parseInt(lp.revision)>meta.availableLanguages[loc]){localeSources[loc]='extra';}}}
|
||||
if(!(defaultLocale in localeSources)){localeSources[defaultLocale]='app';}
|
||||
return[defaultLocale,localeSources];}
|
||||
function splitAvailableLanguagesString(str){var langs={};str.split(',').forEach(function(lang){lang=lang.trim().split(':');langs[lang[0]]=parseInt(lang[1]);});return langs;}
|
||||
function onMetaInjected(node,meta){switch(node.getAttribute('name')){case'availableLanguages':meta.availableLanguages=splitAvailableLanguagesString(node.getAttribute('content'));break;case'defaultLanguage':meta.defaultLanguage=node.getAttribute('content');break;case'appVersion':navigator.mozL10n._config.appVersion=node.getAttribute('content');break;}}
|
||||
function onScriptInjected(node){var lang=node.getAttribute('lang');var locale=this.ctx.getLocale(lang);locale.addAST(JSON.parse(node.textContent));}
|
||||
function initLocale(){this.ctx.requestLocales.apply(this.ctx,navigator.languages||[navigator.language]);window.addEventListener('languagechange',function l10n_langchange(){this.ctx.requestLocales.apply(this.ctx,navigator.languages||[navigator.language]);}.bind(this));}
|
||||
function localizeMutations(mutations){var mutation;var targets=new Set();for(var i=0;i<mutations.length;i++){mutation=mutations[i];if(mutation.type==='childList'){var addedNode;for(var j=0;j<mutation.addedNodes.length;j++){addedNode=mutation.addedNodes[j];if(addedNode.nodeType!==Node.ELEMENT_NODE){continue;}
|
||||
targets.add(addedNode);}}
|
||||
if(mutation.type==='attributes'){targets.add(mutation.target);}}
|
||||
targets.forEach(function(target){if(target.childElementCount){translateFragment.call(this,target);}else if(target.hasAttribute('data-l10n-id')){translateElement.call(this,target);}},this);}
|
||||
function onMutations(mutations,self){self.disconnect();localizeMutations.call(this,mutations);self.observe(document,moConfig);}
|
||||
function onReady(){if(!navigator.mozL10n._config.isPretranslated){translateDocument.call(this);}
|
||||
navigator.mozL10n._config.isPretranslated=false;if(pendingElements){for(var i=0,element;element=pendingElements[i];i++){translateElement.call(this,element);}
|
||||
pendingElements=null;}
|
||||
if(!nodeObserver){initObserver();}
|
||||
fireLocalizedEvent.call(this);}
|
||||
function fireLocalizedEvent(){var event=new CustomEvent('localized',{'bubbles':false,'cancelable':false,'detail':{'language':this.ctx.supportedLocales[0]}});window.dispatchEvent(event);}
|
||||
var reOverlay=/<|&#?\w+;/;var reHtml=/[&<>]/g;var htmlEntities={'&':'&','<':'<','>':'>',};function translateDocument(){document.documentElement.lang=this.language.code;document.documentElement.dir=this.language.direction;translateFragment.call(this,document.documentElement);}
|
||||
function translateFragment(element){if(typeof element.hasAttribute==='function'&&element.hasAttribute('data-l10n-id')){translateElement.call(this,element);}
|
||||
var nodes=getTranslatableChildren(element);for(var i=0;i<nodes.length;i++){translateElement.call(this,nodes[i]);}}
|
||||
function setL10nAttributes(element,id,args){element.setAttribute('data-l10n-id',id);if(args){element.setAttribute('data-l10n-args',JSON.stringify(args));}}
|
||||
function getL10nAttributes(element){return{id:element.getAttribute('data-l10n-id'),args:JSON.parse(element.getAttribute('data-l10n-args'))};}
|
||||
function getTranslatableChildren(element){return element?element.querySelectorAll('*[data-l10n-id]'):[];}
|
||||
function camelCaseToDashed(string){if(string==='ariaValueText'){return'aria-valuetext';}
|
||||
return string.replace(/[A-Z]/g,function(match){return'-'+match.toLowerCase();}).replace(/^-/,'');}
|
||||
function escapeL10nArgs(match){return htmlEntities[match];}
|
||||
function translateElement(element){if(!this.ctx.isReady){if(!pendingElements){pendingElements=[];}
|
||||
pendingElements.push(element);return;}
|
||||
var l10nId=element.getAttribute('data-l10n-id');if(!l10nId){return false;}
|
||||
var l10nArgs=element.getAttribute('data-l10n-args');var entity=this.ctx.getEntity(l10nId,l10nArgs?JSON.parse(l10nArgs.replace(reHtml,escapeL10nArgs)):undefined);var value=entity.value;if(typeof value==='string'){if(!reOverlay.test(value)){element.textContent=value;}else{var translation=element.ownerDocument.createElement('template');translation.innerHTML=value;overlayElement(element,translation.content);}}
|
||||
for(var key in entity.attrs){var attrName=camelCaseToDashed(key);if(isAttrAllowed({name:attrName},element)){element.setAttribute(attrName,entity.attrs[key]);}}}
|
||||
function overlayElement(sourceElement,translationElement){var result=translationElement.ownerDocument.createDocumentFragment();var k,attr;var childElement;while((childElement=translationElement.childNodes[0])){translationElement.removeChild(childElement);if(childElement.nodeType===Node.TEXT_NODE){result.appendChild(childElement);continue;}
|
||||
var index=getIndexOfType(childElement);var sourceChild=getNthElementOfType(sourceElement,childElement,index);if(sourceChild){overlayElement(sourceChild,childElement);result.appendChild(sourceChild);continue;}
|
||||
if(isElementAllowed(childElement)){var sanitizedChild=childElement.ownerDocument.createElement(childElement.nodeName);overlayElement(sanitizedChild,childElement);result.appendChild(sanitizedChild);continue;}
|
||||
result.appendChild(document.createTextNode(childElement.textContent));}
|
||||
sourceElement.textContent='';sourceElement.appendChild(result);if(translationElement.attributes){for(k=0,attr;(attr=translationElement.attributes[k]);k++){if(isAttrAllowed(attr,sourceElement)){sourceElement.setAttribute(attr.name,attr.value);}}}}
|
||||
function isElementAllowed(element){return allowed.elements.indexOf(element.tagName.toLowerCase())!==-1;}
|
||||
function isAttrAllowed(attr,element){var attrName=attr.name.toLowerCase();var tagName=element.tagName.toLowerCase();if(allowed.attributes.global.indexOf(attrName)!==-1){return true;}
|
||||
if(!allowed.attributes[tagName]){return false;}
|
||||
if(allowed.attributes[tagName].indexOf(attrName)!==-1){return true;}
|
||||
if(tagName==='input'&&attrName==='value'){var type=element.type.toLowerCase();if(type==='submit'||type==='button'||type==='reset'){return true;}}
|
||||
return false;}
|
||||
function getNthElementOfType(context,element,index){var nthOfType=0;for(var i=0,child;child=context.children[i];i++){if(child.nodeType===Node.ELEMENT_NODE&&child.tagName===element.tagName){if(nthOfType===index){return child;}
|
||||
nthOfType++;}}
|
||||
return null;}
|
||||
function getIndexOfType(element){var index=0;var child;while((child=element.previousElementSibling)){if(child.tagName===element.tagName){index++;}}
|
||||
return index;}
|
||||
var DEBUG=false;navigator.mozL10n.ctx=new Context(window.document?document.URL:null);navigator.mozL10n.ctx.ready(onReady.bind(navigator.mozL10n));navigator.mozL10n.ctx.addEventListener('notfounderror',function reportMissingEntity(e){if(DEBUG||e.loc==='en-US'){console.warn(e.toString());}});if(DEBUG){navigator.mozL10n.ctx.addEventListener('fetcherror',console.error.bind(console));navigator.mozL10n.ctx.addEventListener('parseerror',console.error.bind(console));navigator.mozL10n.ctx.addEventListener('resolveerror',console.error.bind(console));}
|
||||
if(window.document){navigator.mozL10n._config.isPretranslated=document.documentElement.lang===navigator.language;var forcePretranslate=!navigator.mozL10n._config.isPretranslated;whenInteractive(init.bind(navigator.mozL10n,forcePretranslate));}
|
||||
document.l10n={setAttributes:navigator.mozL10n.setAttributes,getAttributes:navigator.mozL10n.getAttributes,formatValue:function(id,args){return navigator.mozL10n.formatValue(id,args);},translateFragment:function(frag){return Promise.resolve(navigator.mozL10n.translateFragment(frag));},ready:new Promise(function(resolve){navigator.mozL10n.once(resolve);}),formatValues:function(){var keys=arguments;var resp=keys.map(function(key){if(Array.isArray(key)){return navigator.mozL10n.formatValue(key[0],key[1]);}
|
||||
return navigator.mozL10n.formatValue(key);});return Promise.all(resp);},requestLanguages:function(langs){navigator.mozL10n.ctx.requestLocales.apply(navigator.mozL10n.ctx,langs);},pseudo:{'fr-x-psaccent':{getName:function(){return Promise.resolve(navigator.mozL10n.qps['fr-x-psaccent'].name);},processString:function(s){return Promise.resolve(navigator.mozL10n.qps['fr-x-psaccent'].translate(s));}},'ar-x-psbidi':{getName:function(){return Promise.resolve(navigator.mozL10n.qps['ar-x-psbidi'].name);},processString:function(s){return Promise.resolve(navigator.mozL10n.qps['ar-x-psbidi'].translate(s));}}},};navigator.mozL10n.ready(function(){document.documentElement.setAttribute('langs',navigator.mozL10n.ctx.supportedLocales.join(' '));});navigator.mozL10n.once(function(){window.addEventListener('localized',function(){document.dispatchEvent(new CustomEvent('DOMRetranslated',{bubbles:false,cancelable:false}));});});})(this);;;this.asyncStorage=(function(){'use strict';var DBNAME='asyncStorage';var DBVERSION=1;var STORENAME='keyvaluepairs';var db=null;function withDatabase(f){if(db){f();}else{var openreq=indexedDB.open(DBNAME,DBVERSION);openreq.onerror=function withStoreOnError(){console.error('asyncStorage: can\'t open database:',openreq.error.name);};openreq.onupgradeneeded=function withStoreOnUpgradeNeeded(){openreq.result.createObjectStore(STORENAME);};openreq.onsuccess=function withStoreOnSuccess(){db=openreq.result;f();};}}
|
||||
function withStore(type,callback,oncomplete){withDatabase(function(){var transaction=db.transaction(STORENAME,type);if(oncomplete){transaction.oncomplete=oncomplete;}
|
||||
callback(transaction.objectStore(STORENAME));});}
|
||||
function getItem(key,callback){var req;withStore('readonly',function getItemBody(store){req=store.get(key);req.onerror=function getItemOnError(){console.error('Error in asyncStorage.getItem(): ',req.error.name);};},function onComplete(){var value=req.result;if(value===undefined){value=null;}
|
||||
callback(value);});}
|
||||
function setItem(key,value,callback){withStore('readwrite',function setItemBody(store){var req=store.put(value,key);req.onerror=function setItemOnError(){console.error('Error in asyncStorage.setItem(): ',req.error.name);};},callback);}
|
||||
function removeItem(key,callback){withStore('readwrite',function removeItemBody(store){var req=store.delete(key);req.onerror=function removeItemOnError(){console.error('Error in asyncStorage.removeItem(): ',req.error.name);};},callback);}
|
||||
function clear(callback){withStore('readwrite',function clearBody(store){var req=store.clear();req.onerror=function clearOnError(){console.error('Error in asyncStorage.clear(): ',req.error.name);};},callback);}
|
||||
function length(callback){var req;withStore('readonly',function lengthBody(store){req=store.count();req.onerror=function lengthOnError(){console.error('Error in asyncStorage.length(): ',req.error.name);};},function onComplete(){callback(req.result);});}
|
||||
function key(n,callback){if(n<0){callback(null);return;}
|
||||
var req;withStore('readonly',function keyBody(store){var advanced=false;req=store.openCursor();req.onsuccess=function keyOnSuccess(){var cursor=req.result;if(!cursor){return;}
|
||||
if(n===0||advanced){return;}
|
||||
advanced=true;cursor.advance(n);};req.onerror=function keyOnError(){console.error('Error in asyncStorage.key(): ',req.error.name);};},function onComplete(){var cursor=req.result;callback(cursor?cursor.key:null);});}
|
||||
return{getItem:getItem,setItem:setItem,removeItem:removeItem,clear:clear,length:length,key:key};}());;;;'use strict';var LazyLoader=(function(){function LazyLoader(){this._loaded={};this._isLoading={};}
|
||||
LazyLoader.prototype={_js:function(file,callback){var script=document.createElement('script');script.src=file;script.async=false;script.addEventListener('load',callback);document.head.appendChild(script);this._isLoading[file]=script;},_css:function(file,callback){var style=document.createElement('link');style.type='text/css';style.rel='stylesheet';style.href=file;document.head.appendChild(style);callback();},_html:function(domNode,callback){if(domNode.getAttribute('is')){this.load(['/shared/js/html_imports.js'],function(){HtmlImports.populate(callback);}.bind(this));return;}
|
||||
for(var i=0;i<domNode.childNodes.length;i++){if(domNode.childNodes[i].nodeType==document.COMMENT_NODE){domNode.innerHTML=domNode.childNodes[i].nodeValue;break;}}
|
||||
window.dispatchEvent(new CustomEvent('lazyload',{detail:domNode}));callback();},getJSON:function(file,mozSystem){return new Promise(function(resolve,reject){var xhr;if(mozSystem){xhr=new XMLHttpRequest({mozSystem:true});}else{xhr=new XMLHttpRequest();}
|
||||
xhr.open('GET',file,true);xhr.responseType='json';xhr.onerror=function(error){reject(error);};xhr.onload=function(){if(xhr.response!==null){resolve(xhr.response);}else{reject(new Error('No valid JSON object was found ('+
|
||||
xhr.status+' '+xhr.statusText+')'));}};xhr.send();});},load:function(files,callback){var deferred={};deferred.promise=new Promise(resolve=>{deferred.resolve=resolve;});if(!Array.isArray(files)){files=[files];}
|
||||
var loadsRemaining=files.length,self=this;function perFileCallback(file){if(self._isLoading[file]){delete self._isLoading[file];}
|
||||
self._loaded[file]=true;if(--loadsRemaining===0){deferred.resolve();if(callback){callback();}}}
|
||||
for(var i=0;i<files.length;i++){var file=files[i];if(this._loaded[file.id||file]){perFileCallback(file);}else if(this._isLoading[file]){this._isLoading[file].addEventListener('load',perFileCallback.bind(null,file));}else{var method,idx;if(typeof file==='string'){method=file.match(/\.([^.]+)$/)[1];idx=file;}else{method='html';idx=file.id;}
|
||||
this['_'+method](file,perFileCallback.bind(null,idx));}}
|
||||
return deferred.promise;}};return new LazyLoader();}());;;'use strict';if(typeof window.oauthflow==='undefined'){(function(document){var OAuthFlow=window.oauthflow={};var OAUTH_REDIRECT='redirectURI';var ENDPOINT='loginPage';var APP_ID='applicationId';var SCOPE='scope';var APP_ORIGIN=location.origin;OAuthFlow.init=function(service){var hash=document.location.hash.substring(1);var parameters={};var dataStart=hash.indexOf('access_token');if(dataStart!==-1){var elements=hash.split('&');elements.forEach(function(p){var values=p.split('=');parameters[values[0]]=values[1];});window.opener.postMessage(parameters,APP_ORIGIN);window.close();}};OAuthFlow.start=function(state,service){getAccessToken(state,service);};function getAccessToken(state,service){startOAuth(state,service);}
|
||||
function startOAuth(state,service){var params=oauthflow.params[service];var redirect_uri=encodeURIComponent(params[OAUTH_REDIRECT]);var scope=params[SCOPE].join(',');var scopeParam=encodeURIComponent(scope);var queryParams=['client_id='+params[APP_ID],'redirect_uri='+redirect_uri,'response_type=token','scope='+scopeParam,'approval_prompt=force','state='+state];var query=queryParams.join('&');var url=params[ENDPOINT]+query;window.open(url,'','dialog');}})(document);};;'use strict';var fb=window.fb||{};if(typeof window.oauth2==='undefined'){(function(document){var oauth2=window.oauth2={};var accessTokenCbData;var STORAGE_KEY='tokenData';var APP_ORIGIN=location.origin;function clearStorage(service){ImportStatusData.remove(getKey(service));}
|
||||
function getKey(service){var key=STORAGE_KEY;if(service!=='facebook'){key=STORAGE_KEY+'_'+service;}
|
||||
return key;}
|
||||
oauth2.getAccessToken=function(ready,state,service){accessTokenCbData={callback:ready,state:state,service:service};fb.testToken=fb.testToken||(parent.fb&&parent.fb.testToken);if(service==='facebook'&&typeof fb.testToken==='string'&&fb.testToken.trim().length>0){window.console.warn('Facebook. A test token will be used!');tokenDataReady({data:{access_token:fb.testToken,expires_in:0,state:state}});return;}
|
||||
ImportStatusData.get(getKey(service)).then(function getAccessToken(tokenData){if(!tokenData||!tokenData.access_token){startOAuth(state,service);return;}
|
||||
var access_token=tokenData.access_token;var expires=Number(tokenData.expires);var token_ts=tokenData.token_ts;if(expires!==0&&Date.now()-token_ts>=expires){window.console.warn('Access token has expired, restarting flow');startOAuth(state,service);return;}
|
||||
if(typeof ready==='function'){ready(access_token);}});};function startOAuth(state,service){clearStorage(service);oauthflow.start(state,service);}
|
||||
function tokenDataReady(e){var parameters=e.data;if(!parameters||!parameters.access_token){return;}
|
||||
if(e.origin!==APP_ORIGIN){return;}
|
||||
Curtain.show('wait',accessTokenCbData.state);window.setTimeout(function do_token_ready(){var access_token=parameters.access_token;if(parameters.state===accessTokenCbData.state){accessTokenCbData.callback(access_token);}else{window.console.error('FB: Error in state',parameters.state,accessTokenCbData.state);return;}
|
||||
var end=parameters.expires_in;ImportStatusData.put(getKey(accessTokenCbData.service),{access_token:access_token,expires:end*1000,token_ts:Date.now()}).then(function notify_parent(){parent.postMessage({type:'token_stored',data:''},APP_ORIGIN);});},0);}
|
||||
window.addEventListener('message',tokenDataReady);})(document);};;var oauthflow=this.oauthflow||{};oauthflow.params={"facebook":{"redirectURI":"https://www.facebook.com/connect/login_success.html","loginPage":"https://m.facebook.com/dialog/oauth/?","applicationId":"123456","scope":["friends_about_me","friends_birthday","friends_hometown","friends_location","friends_work_history","read_stream"],"redirectMsg":"http://intense-tundra-4122.herokuapp.com/fbowd/oauth2_new/dialogs_end.html","redirectLogout":"http://intense-tundra-4122.herokuapp.com/fbowd/oauth2_new/dialogs_end.html","imgDetailWidth":200,"imgThumbSize":120},"live":{"redirectURI":"https://www.mozilla.org/index.html","loginPage":"https://login.live.com/oauth20_authorize.srf?","applicationId":"123456","scope":["wl.basic","wl.contacts_emails","wl.contacts_phone_numbers","wl.contacts_birthday","wl.contacts_postal_addresses"],"logoutUrl":"https://login.live.com/logout.srf"},"gmail":{"redirectURI":"https://serene-cove-3587.herokuapp.com/liveowd/oauth2_new/flow_live.html","loginPage":"https://accounts.google.com/o/oauth2/auth?","applicationId":"664741361278.apps.googleusercontent.com","scope":["https://www.google.com/m8/feeds/"],"logoutUrl":"https://accounts.google.com/Logout"}};;;'use strict';var Curtain=(function(){var curtainFrame=parent.document.querySelector('#iframe_curtain');if(!curtainFrame){curtainFrame=document.createElement('iframe');curtainFrame.id='iframe_curtain';curtainFrame.src='/shared/pages/import/curtain.html';parent.document.body.appendChild(curtainFrame);}
|
||||
function getDoc(){return curtainFrame.contentWindow.document;}
|
||||
var cpuWakeLock,cancelButton,retryButton,okButton,progressElement,form,progressTitle;var messages=[];var elements=['error','timeout','wait','message','progress','alert'];if(getDoc().readyState==='complete'){init();}else{curtainFrame.addEventListener('load',function loaded(){curtainFrame.removeEventListener('load',loaded);init();});}
|
||||
function init(){var doc=getDoc();cancelButton=doc.querySelector('#cancel');retryButton=doc.querySelector('#retry');okButton=doc.querySelector('#ok');progressElement=doc.querySelector('#progressElement');form=doc.querySelector('form');elements.forEach(function createElementRef(name){messages[name]=doc.getElementById(name+'Msg');});progressTitle=doc.getElementById('progressTitle');}
|
||||
function doShow(type){form.classList.remove('no-menu');form.dataset.state=type;curtainFrame.classList.add('visible');curtainFrame.classList.remove('fade-out');curtainFrame.classList.add('fade-in');}
|
||||
function capitalize(str){return str.charAt(0).toUpperCase()+str.slice(1);}
|
||||
function Progress(pfrom){var from=pfrom;var counter=0;var total=0;progressElement.setAttribute('value',0);function showMessage(){document.l10n.setAttributes(messages.progress,'progressFB',{current:counter,total:total});}
|
||||
this.update=function(){progressElement.setAttribute('value',(++counter*100)/total);showMessage();};this.setFrom=function(pfrom){from=capitalize(pfrom);progressTitle.setAttribute('data-l10n-id','progressFB3'+from+'Title');};this.setTotal=function(ptotal){total=ptotal;showMessage();};this.getValue=function(){return counter;};}
|
||||
return{show:function(type,from){var out;from=capitalize(from);switch(type){case'wait':messages[type].setAttribute('data-l10n-id',type+from);break;case'timeout':document.l10n.formatValue('timeout'+from).then((from)=>{document.l10n.setAttributes(messages[type],'timeout1',{from:from});});break;case'error':document.l10n.formatValue('type'+from).then((from)=>{document.l10n.setAttributes(messages[type],'error1',{from:from});});break;case'alert':case'message':messages[type].setAttribute('data-l10n-id',type+from);break;case'progress':progressTitle.setAttribute('data-l10n-id',type+'FB3'+from+'Title');out=new Progress(from);cpuWakeLock=navigator.requestWakeLock('cpu');break;}
|
||||
doShow(type);return out;},hide:function c_hide(hiddenCB){if(cpuWakeLock){cpuWakeLock.unlock();cpuWakeLock=null;}
|
||||
curtainFrame.classList.add('fade-out');curtainFrame.addEventListener('animationend',function cu_fadeOut(ev){curtainFrame.removeEventListener('animationend',cu_fadeOut);curtainFrame.classList.remove('visible');curtainFrame.classList.remove('fade-out');curtainFrame.classList.remove('fade-in');delete form.dataset.state;if(typeof hiddenCB==='function'){hiddenCB();}});},set oncancel(cancelCb){if(typeof cancelCb==='function'){delete cancelButton.onclick;cancelButton.onclick=function on_cancel(e){delete cancelButton.onclick;cancelCb();return false;};}},set onretry(retryCb){if(typeof retryCb==='function'){delete retryButton.onclick;retryButton.onclick=function on_retry(e){delete retryButton.onclick;retryCb();return false;};}},set onok(okCb){if(typeof okCb==='function'){delete okButton.onclick;okButton.onclick=function on_ok(e){delete okButton.onclick;okCb();return false;};}},get visible(){return curtainFrame.classList.contains('visible');},hideMenu:function c_hideMenu(){form.classList.add('no-menu');}};})();
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
;;(function(window,undefined){'use strict';function L10nError(message,id,loc){this.name='L10nError';this.message=message;this.id=id;this.loc=loc;}
|
||||
L10nError.prototype=Object.create(Error.prototype);L10nError.prototype.constructor=L10nError;var io={_load:function(type,url,callback,sync){var xhr=new XMLHttpRequest();var needParse;if(xhr.overrideMimeType){xhr.overrideMimeType(type);}
|
||||
xhr.open('GET',url,!sync);if(type==='application/json'){if(sync){needParse=true;}else{xhr.responseType='json';}}
|
||||
xhr.addEventListener('load',function io_onload(e){if(e.target.status===200||e.target.status===0){var res=e.target.response||e.target.responseText;callback(null,needParse?JSON.parse(res):res);}else{callback(new L10nError('Not found: '+url));}});xhr.addEventListener('error',callback);xhr.addEventListener('timeout',callback);try{xhr.send(null);}catch(e){if(e.name==='NS_ERROR_FILE_NOT_FOUND'){callback(new L10nError('Not found: '+url));}else{throw e;}}},load:function(url,callback,sync){return io._load('text/plain',url,callback,sync);},loadJSON:function(url,callback,sync){return io._load('application/json',url,callback,sync);}};function EventEmitter(){}
|
||||
EventEmitter.prototype.emit=function ee_emit(){if(!this._listeners){return;}
|
||||
var args=Array.prototype.slice.call(arguments);var type=args.shift();if(!this._listeners[type]){return;}
|
||||
var typeListeners=this._listeners[type].slice();for(var i=0;i<typeListeners.length;i++){typeListeners[i].apply(this,args);}};EventEmitter.prototype.addEventListener=function ee_add(type,listener){if(!this._listeners){this._listeners={};}
|
||||
if(!(type in this._listeners)){this._listeners[type]=[];}
|
||||
this._listeners[type].push(listener);};EventEmitter.prototype.removeEventListener=function ee_rm(type,listener){if(!this._listeners){return;}
|
||||
var typeListeners=this._listeners[type];var pos=typeListeners.indexOf(listener);if(pos===-1){return;}
|
||||
typeListeners.splice(pos,1);};function getPluralRule(lang){var locales2rules={'af':3,'ak':4,'am':4,'ar':1,'asa':3,'az':0,'be':11,'bem':3,'bez':3,'bg':3,'bh':4,'bm':0,'bn':3,'bo':0,'br':20,'brx':3,'bs':11,'ca':3,'cgg':3,'chr':3,'cs':12,'cy':17,'da':3,'de':3,'dv':3,'dz':0,'ee':3,'el':3,'en':3,'eo':3,'es':3,'et':3,'eu':3,'fa':0,'ff':5,'fi':3,'fil':4,'fo':3,'fr':5,'fur':3,'fy':3,'ga':8,'gd':24,'gl':3,'gsw':3,'gu':3,'guw':4,'gv':23,'ha':3,'haw':3,'he':2,'hi':4,'hr':11,'hu':0,'id':0,'ig':0,'ii':0,'is':3,'it':3,'iu':7,'ja':0,'jmc':3,'jv':0,'ka':0,'kab':5,'kaj':3,'kcg':3,'kde':0,'kea':0,'kk':3,'kl':3,'km':0,'kn':0,'ko':0,'ksb':3,'ksh':21,'ku':3,'kw':7,'lag':18,'lb':3,'lg':3,'ln':4,'lo':0,'lt':10,'lv':6,'mas':3,'mg':4,'mk':16,'ml':3,'mn':3,'mo':9,'mr':3,'ms':0,'mt':15,'my':0,'nah':3,'naq':7,'nb':3,'nd':3,'ne':3,'nl':3,'nn':3,'no':3,'nr':3,'nso':4,'ny':3,'nyn':3,'om':3,'or':3,'pa':3,'pap':3,'pl':13,'ps':3,'pt':3,'rm':3,'ro':9,'rof':3,'ru':11,'rwk':3,'sah':0,'saq':3,'se':7,'seh':3,'ses':0,'sg':0,'sh':11,'shi':19,'sk':12,'sl':14,'sma':7,'smi':7,'smj':7,'smn':7,'sms':7,'sn':3,'so':3,'sq':3,'sr':11,'ss':3,'ssy':3,'st':3,'sv':3,'sw':3,'syr':3,'ta':3,'te':3,'teo':3,'th':0,'ti':4,'tig':3,'tk':3,'tl':4,'tn':3,'to':0,'tr':0,'ts':3,'tzm':22,'uk':11,'ur':3,'ve':3,'vi':0,'vun':3,'wa':4,'wae':3,'wo':0,'xh':3,'xog':3,'yo':0,'zh':0,'zu':3};function isIn(n,list){return list.indexOf(n)!==-1;}
|
||||
function isBetween(n,start,end){return typeof n===typeof start&&start<=n&&n<=end;}
|
||||
var pluralRules={'0':function(){return'other';},'1':function(n){if((isBetween((n%100),3,10))){return'few';}
|
||||
if(n===0){return'zero';}
|
||||
if((isBetween((n%100),11,99))){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'2':function(n){if(n!==0&&(n%10)===0){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'3':function(n){if(n===1){return'one';}
|
||||
return'other';},'4':function(n){if((isBetween(n,0,1))){return'one';}
|
||||
return'other';},'5':function(n){if((isBetween(n,0,2))&&n!==2){return'one';}
|
||||
return'other';},'6':function(n){if(n===0){return'zero';}
|
||||
if((n%10)===1&&(n%100)!==11){return'one';}
|
||||
return'other';},'7':function(n){if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'8':function(n){if((isBetween(n,3,6))){return'few';}
|
||||
if((isBetween(n,7,10))){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'9':function(n){if(n===0||n!==1&&(isBetween((n%100),1,19))){return'few';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'10':function(n){if((isBetween((n%10),2,9))&&!(isBetween((n%100),11,19))){return'few';}
|
||||
if((n%10)===1&&!(isBetween((n%100),11,19))){return'one';}
|
||||
return'other';},'11':function(n){if((isBetween((n%10),2,4))&&!(isBetween((n%100),12,14))){return'few';}
|
||||
if((n%10)===0||(isBetween((n%10),5,9))||(isBetween((n%100),11,14))){return'many';}
|
||||
if((n%10)===1&&(n%100)!==11){return'one';}
|
||||
return'other';},'12':function(n){if((isBetween(n,2,4))){return'few';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'13':function(n){if((isBetween((n%10),2,4))&&!(isBetween((n%100),12,14))){return'few';}
|
||||
if(n!==1&&(isBetween((n%10),0,1))||(isBetween((n%10),5,9))||(isBetween((n%100),12,14))){return'many';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'14':function(n){if((isBetween((n%100),3,4))){return'few';}
|
||||
if((n%100)===2){return'two';}
|
||||
if((n%100)===1){return'one';}
|
||||
return'other';},'15':function(n){if(n===0||(isBetween((n%100),2,10))){return'few';}
|
||||
if((isBetween((n%100),11,19))){return'many';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'16':function(n){if((n%10)===1&&n!==11){return'one';}
|
||||
return'other';},'17':function(n){if(n===3){return'few';}
|
||||
if(n===0){return'zero';}
|
||||
if(n===6){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'18':function(n){if(n===0){return'zero';}
|
||||
if((isBetween(n,0,2))&&n!==0&&n!==2){return'one';}
|
||||
return'other';},'19':function(n){if((isBetween(n,2,10))){return'few';}
|
||||
if((isBetween(n,0,1))){return'one';}
|
||||
return'other';},'20':function(n){if((isBetween((n%10),3,4)||((n%10)===9))&&!(isBetween((n%100),10,19)||isBetween((n%100),70,79)||isBetween((n%100),90,99))){return'few';}
|
||||
if((n%1000000)===0&&n!==0){return'many';}
|
||||
if((n%10)===2&&!isIn((n%100),[12,72,92])){return'two';}
|
||||
if((n%10)===1&&!isIn((n%100),[11,71,91])){return'one';}
|
||||
return'other';},'21':function(n){if(n===0){return'zero';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'22':function(n){if((isBetween(n,0,1))||(isBetween(n,11,99))){return'one';}
|
||||
return'other';},'23':function(n){if((isBetween((n%10),1,2))||(n%20)===0){return'one';}
|
||||
return'other';},'24':function(n){if((isBetween(n,3,10)||isBetween(n,13,19))){return'few';}
|
||||
if(isIn(n,[2,12])){return'two';}
|
||||
if(isIn(n,[1,11])){return'one';}
|
||||
return'other';}};var index=locales2rules[lang.replace(/-.*$/,'')];if(!(index in pluralRules)){return function(){return'other';};}
|
||||
return pluralRules[index];}
|
||||
var MAX_PLACEABLES=100;var PropertiesParser={patterns:null,entryIds:null,init:function(){this.patterns={comment:/^\s*#|^\s*$/,entity:/^([^=\s]+)\s*=\s*(.*)$/,multiline:/[^\\]\\$/,index:/\{\[\s*(\w+)(?:\(([^\)]*)\))?\s*\]\}/i,unicode:/\\u([0-9a-fA-F]{1,4})/g,entries:/[^\r\n]+/g,controlChars:/\\([\\\n\r\t\b\f\{\}\"\'])/g,placeables:/\{\{\s*([^\s]*?)\s*\}\}/,};},parse:function(ctx,source){if(!this.patterns){this.init();}
|
||||
var ast=[];this.entryIds=Object.create(null);var entries=source.match(this.patterns.entries);if(!entries){return ast;}
|
||||
for(var i=0;i<entries.length;i++){var line=entries[i];if(this.patterns.comment.test(line)){continue;}
|
||||
while(this.patterns.multiline.test(line)&&i<entries.length){line=line.slice(0,-1)+entries[++i].trim();}
|
||||
var entityMatch=line.match(this.patterns.entity);if(entityMatch){try{this.parseEntity(entityMatch[1],entityMatch[2],ast);}catch(e){if(ctx){ctx._emitter.emit('parseerror',e);}else{throw e;}}}}
|
||||
return ast;},parseEntity:function(id,value,ast){var name,key;var pos=id.indexOf('[');if(pos!==-1){name=id.substr(0,pos);key=id.substring(pos+1,id.length-1);}else{name=id;key=null;}
|
||||
var nameElements=name.split('.');if(nameElements.length>2){throw new L10nError('Error in ID: "'+name+'".'+' Nested attributes are not supported.');}
|
||||
var attr;if(nameElements.length>1){name=nameElements[0];attr=nameElements[1];if(attr[0]==='$'){throw new L10nError('Attribute can\'t start with "$"',id);}}else{attr=null;}
|
||||
this.setEntityValue(name,attr,key,this.unescapeString(value),ast);},setEntityValue:function(id,attr,key,rawValue,ast){var pos,v;var value=rawValue.indexOf('{{')>-1?this.parseString(rawValue):rawValue;if(attr){pos=this.entryIds[id];if(pos===undefined){v={$i:id};if(key){v[attr]={};v[attr][key]=value;}else{v[attr]=value;}
|
||||
ast.push(v);this.entryIds[id]=ast.length-1;return;}
|
||||
if(key){if(typeof(ast[pos][attr])==='string'){ast[pos][attr]={$x:this.parseIndex(ast[pos][attr]),$v:{}};}
|
||||
ast[pos][attr].$v[key]=value;return;}
|
||||
ast[pos][attr]=value;return;}
|
||||
if(key){pos=this.entryIds[id];if(pos===undefined){v={};v[key]=value;ast.push({$i:id,$v:v});this.entryIds[id]=ast.length-1;return;}
|
||||
if(typeof(ast[pos].$v)==='string'){ast[pos].$x=this.parseIndex(ast[pos].$v);ast[pos].$v={};}
|
||||
ast[pos].$v[key]=value;return;}
|
||||
ast.push({$i:id,$v:value});this.entryIds[id]=ast.length-1;},parseString:function(str){var chunks=str.split(this.patterns.placeables);var complexStr=[];var len=chunks.length;var placeablesCount=(len-1)/2;if(placeablesCount>=MAX_PLACEABLES){throw new L10nError('Too many placeables ('+placeablesCount+', max allowed is '+MAX_PLACEABLES+')');}
|
||||
for(var i=0;i<chunks.length;i++){if(chunks[i].length===0){continue;}
|
||||
if(i%2===1){complexStr.push({t:'idOrVar',v:chunks[i]});}else{complexStr.push(chunks[i]);}}
|
||||
return complexStr;},unescapeString:function(str){if(str.lastIndexOf('\\')!==-1){str=str.replace(this.patterns.controlChars,'$1');}
|
||||
return str.replace(this.patterns.unicode,function(match,token){return unescape('%u'+'0000'.slice(token.length)+token);});},parseIndex:function(str){var match=str.match(this.patterns.index);if(!match){throw new L10nError('Malformed index');}
|
||||
if(match[2]){return[{t:'idOrVar',v:match[1]},match[2]];}else{return[{t:'idOrVar',v:match[1]}];}}};var KNOWN_MACROS=['plural'];var MAX_PLACEABLE_LENGTH=2500;var rePlaceables=/\{\{\s*(.+?)\s*\}\}/g;var nonLatin1=/[^\x01-\xFF]/;var FSI='\u2068';var PDI='\u2069';function createEntry(node,env){var keys=Object.keys(node);if(typeof node.$v==='string'&&keys.length===2){return node.$v;}
|
||||
var attrs;for(var i=0,key;key=keys[i];i++){if(key[0]==='$'){continue;}
|
||||
if(!attrs){attrs=Object.create(null);}
|
||||
attrs[key]=createAttribute(node[key],env,node.$i+'.'+key);}
|
||||
return{id:node.$i,value:node.$v!==undefined?node.$v:null,index:node.$x||null,attrs:attrs||null,env:env,dirty:false};}
|
||||
function createAttribute(node,env,id){if(typeof node==='string'){return node;}
|
||||
return{id:id,value:node.$v||(node!==undefined?node:null),index:node.$x||null,env:env,dirty:false};}
|
||||
function format(args,entity){if(typeof entity==='string'){return[{},entity];}
|
||||
if(entity.dirty){throw new L10nError('Cyclic reference detected: '+entity.id);}
|
||||
entity.dirty=true;var rv;try{rv=resolveValue({},args,entity.env,entity.value,entity.index);}finally{entity.dirty=false;}
|
||||
return rv;}
|
||||
function resolveIdentifier(args,env,id){if(KNOWN_MACROS.indexOf(id)>-1){return[{},env['__'+id]];}
|
||||
if(args&&args.hasOwnProperty(id)){if(typeof args[id]==='string'||(typeof args[id]==='number'&&!isNaN(args[id]))){return[{},args[id]];}else{throw new L10nError('Arg must be a string or a number: '+id);}}
|
||||
if(id in env&&id!=='__proto__'){return format(args,env[id]);}
|
||||
throw new L10nError('Unknown reference: '+id);}
|
||||
function subPlaceable(locals,args,env,id){var res;try{res=resolveIdentifier(args,env,id);}catch(err){return[{error:err},'{{ '+id+' }}'];}
|
||||
var value=res[1];if(typeof value==='number'){return res;}
|
||||
if(typeof value==='string'){if(value.length>=MAX_PLACEABLE_LENGTH){throw new L10nError('Too many characters in placeable ('+
|
||||
value.length+', max allowed is '+
|
||||
MAX_PLACEABLE_LENGTH+')');}
|
||||
if(locals.contextIsNonLatin1||value.match(nonLatin1)){res[1]=FSI+value+PDI;}
|
||||
return res;}
|
||||
return[{},'{{ '+id+' }}'];}
|
||||
function interpolate(locals,args,env,arr){return arr.reduce(function(prev,cur){if(typeof cur==='string'){return[prev[0],prev[1]+cur];}else if(cur.t==='idOrVar'){var placeable=subPlaceable(locals,args,env,cur.v);return[prev[0],prev[1]+placeable[1]];}},[locals,'']);}
|
||||
function resolveSelector(args,env,expr,index){var selectorName=index[0].v;var selector=resolveIdentifier(args,env,selectorName)[1];if(typeof selector!=='function'){return selector;}
|
||||
var argValue=index[1]?resolveIdentifier(args,env,index[1])[1]:undefined;if(selector===env.__plural){if(argValue===0&&'zero'in expr){return'zero';}
|
||||
if(argValue===1&&'one'in expr){return'one';}
|
||||
if(argValue===2&&'two'in expr){return'two';}}
|
||||
return selector(argValue);}
|
||||
function resolveValue(locals,args,env,expr,index){if(!expr){return[locals,expr];}
|
||||
if(typeof expr==='string'||typeof expr==='boolean'||typeof expr==='number'){return[locals,expr];}
|
||||
if(Array.isArray(expr)){locals.contextIsNonLatin1=expr.some(function($_){return typeof($_)==='string'&&$_.match(nonLatin1);});return interpolate(locals,args,env,expr);}
|
||||
if(index){var selector=resolveSelector(args,env,expr,index);if(expr.hasOwnProperty(selector)){return resolveValue(locals,args,env,expr[selector]);}}
|
||||
if('other'in expr){return resolveValue(locals,args,env,expr.other);}
|
||||
throw new L10nError('Unresolvable value');}
|
||||
var Resolver={createEntry:createEntry,format:format,rePlaceables:rePlaceables};function walkContent(node,fn){if(typeof node==='string'){return fn(node);}
|
||||
if(node.t==='idOrVar'){return node;}
|
||||
var rv=Array.isArray(node)?[]:{};var keys=Object.keys(node);for(var i=0,key;(key=keys[i]);i++){if(key==='$i'||key==='$x'){rv[key]=node[key];}else{rv[key]=walkContent(node[key],fn);}}
|
||||
return rv;}
|
||||
var reAlphas=/[a-zA-Z]/g;var reVowels=/[aeiouAEIOU]/g;var ACCENTED_MAP='\u0226\u0181\u0187\u1E12\u1E16\u0191\u0193\u0126\u012A'+'\u0134\u0136\u013F\u1E3E\u0220\u01FE\u01A4\u024A\u0158'+'\u015E\u0166\u016C\u1E7C\u1E86\u1E8A\u1E8E\u1E90'+'[\\]^_`'+'\u0227\u0180\u0188\u1E13\u1E17\u0192\u0260\u0127\u012B'+'\u0135\u0137\u0140\u1E3F\u019E\u01FF\u01A5\u024B\u0159'+'\u015F\u0167\u016D\u1E7D\u1E87\u1E8B\u1E8F\u1E91';var FLIPPED_MAP='\u2200\u0510\u2183p\u018E\u025F\u05E4HI\u017F'+'\u04FC\u02E5WNO\u0500\xD2\u1D1AS\u22A5\u2229\u0245'+'\uFF2DX\u028EZ'+'[\\]\u1D65_,'+'\u0250q\u0254p\u01DD\u025F\u0183\u0265\u0131\u027E'+'\u029E\u0285\u026Fuodb\u0279s\u0287n\u028C\u028Dx\u028Ez';function makeLonger(val){return val.replace(reVowels,function(match){return match+match.toLowerCase();});}
|
||||
function replaceChars(map,val){return val.replace(reAlphas,function(match){return map.charAt(match.charCodeAt(0)-65);});}
|
||||
var reWords=/[^\W0-9_]+/g;function makeRTL(val){return val.replace(reWords,function(match){return'\u202e'+match+'\u202c';});}
|
||||
var reExcluded=/(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/;function mapContent(fn,val){if(!val){return val;}
|
||||
var parts=val.split(reExcluded);var modified=parts.map(function(part){if(reExcluded.test(part)){return part;}
|
||||
return fn(part);});return modified.join('');}
|
||||
function Pseudo(id,name,charMap,modFn){this.id=id;this.translate=mapContent.bind(null,function(val){return replaceChars(charMap,modFn(val));});this.name=this.translate(name);}
|
||||
var PSEUDO={'fr-x-psaccent':new Pseudo('fr-x-psaccent','Runtime Accented',ACCENTED_MAP,makeLonger),'ar-x-psbidi':new Pseudo('ar-x-psbidi','Runtime Bidi',FLIPPED_MAP,makeRTL)};function Locale(id,ctx){this.id=id;this.ctx=ctx;this.isReady=false;this.entries=Object.create(null);this.entries.__plural=getPluralRule(this.isPseudo()?this.ctx.defaultLocale:id);}
|
||||
Locale.prototype.isPseudo=function(){return this.ctx.qps.indexOf(this.id)!==-1;};var bindingsIO={extra:function(id,ver,path,type,callback,errback){if(type==='properties'){type='text';}
|
||||
navigator.mozApps.getLocalizationResource(id,ver,path,type).then(callback.bind(null,null),errback);},app:function(id,ver,path,type,callback,errback,sync){switch(type){case'properties':io.load(path,callback,sync);break;case'json':io.loadJSON(path,callback,sync);break;}},};Locale.prototype.build=function L_build(callback){var sync=!callback;var ctx=this.ctx;var self=this;var l10nLoads=ctx.resLinks.length;function onL10nLoaded(err){if(err){ctx._emitter.emit('fetcherror',err);}
|
||||
if(--l10nLoads<=0){self.isReady=true;if(callback){callback();}}}
|
||||
if(l10nLoads===0){onL10nLoaded();return;}
|
||||
function onJSONLoaded(err,json){if(!err&&json){self.addAST(json);}
|
||||
onL10nLoaded(err);}
|
||||
function onPropLoaded(err,source){if(!err&&source){var ast=PropertiesParser.parse(ctx,source);self.addAST(ast);}
|
||||
onL10nLoaded(err);}
|
||||
var idToFetch=this.isPseudo()?ctx.defaultLocale:this.id;var appVersion=null;var source='app';if(typeof(navigator)!=='undefined'){source=navigator.mozL10n._config.localeSources[this.id]||'app';appVersion=navigator.mozL10n._config.appVersion;}
|
||||
for(var i=0;i<ctx.resLinks.length;i++){var resLink=decodeURI(ctx.resLinks[i]);var path=resLink.replace('{locale}',idToFetch);var type=path.substr(path.lastIndexOf('.')+1);var cb;switch(type){case'json':cb=onJSONLoaded;break;case'properties':cb=onPropLoaded;break;}
|
||||
bindingsIO[source](this.id,appVersion,path,type,cb,onL10nLoaded,sync);}};function createPseudoEntry(node,entries){return Resolver.createEntry(walkContent(node,PSEUDO[this.id].translate),entries);}
|
||||
Locale.prototype.addAST=function(ast){var createEntry=this.isPseudo()?createPseudoEntry.bind(this):Resolver.createEntry;for(var i=0;i<ast.length;i++){this.entries[ast[i].$i]=createEntry(ast[i],this.entries);}};function Context(id){this.id=id;this.isReady=false;this.isLoading=false;this.defaultLocale='en-US';this.availableLocales=[];this.supportedLocales=[];this.qps=[];this.resLinks=[];this.locales={};this._emitter=new EventEmitter();this._ready=new Promise(this.once.bind(this));}
|
||||
function reportMissing(id,err){this._emitter.emit('notfounderror',err);return id;}
|
||||
function getWithFallback(id){var cur=0;var loc;var locale;while(loc=this.supportedLocales[cur]){locale=this.getLocale(loc);if(!locale.isReady){locale.build(null);}
|
||||
var entry=locale.entries[id];if(entry===undefined){cur++;reportMissing.call(this,id,new L10nError('"'+id+'"'+' not found in '+loc+' in '+this.id,id,loc));continue;}
|
||||
return entry;}
|
||||
throw new L10nError('"'+id+'"'+' missing from all supported locales in '+this.id,id);}
|
||||
function formatTuple(args,entity){try{return Resolver.format(args,entity);}catch(err){this._emitter.emit('resolveerror',err);var locals={error:err};return[locals,entity.id];}}
|
||||
function formatValue(args,entity){if(typeof entity==='string'){return entity;}
|
||||
return formatTuple.call(this,args,entity)[1];}
|
||||
function formatEntity(args,entity){var entityTuple=formatTuple.call(this,args,entity);var value=entityTuple[1];var formatted={value:value,attrs:null,};if(entity.attrs){formatted.attrs=Object.create(null);}
|
||||
for(var key in entity.attrs){var attrTuple=formatTuple.call(this,args,entity.attrs[key]);formatted.attrs[key]=attrTuple[1];}
|
||||
return formatted;}
|
||||
function formatAsync(fn,id,args){return this._ready.then(getWithFallback.bind(this,id)).then(fn.bind(this,args),reportMissing.bind(this,id));}
|
||||
Context.prototype.formatValue=function(id,args){return formatAsync.call(this,formatValue,id,args);};Context.prototype.formatEntity=function(id,args){return formatAsync.call(this,formatEntity,id,args);};function legacyGet(fn,id,args){if(!this.isReady){throw new L10nError('Context not ready');}
|
||||
var entry;try{entry=getWithFallback.call(this,id);}catch(err){if(err.loc){throw err;}
|
||||
reportMissing.call(this,id,err);return'';}
|
||||
return fn.call(this,args,entry);}
|
||||
Context.prototype.get=function(id,args){return legacyGet.call(this,formatValue,id,args);};Context.prototype.getEntity=function(id,args){return legacyGet.call(this,formatEntity,id,args);};Context.prototype.getLocale=function getLocale(code){var locales=this.locales;if(locales[code]){return locales[code];}
|
||||
return locales[code]=new Locale(code,this);};function negotiate(available,requested,defaultLocale){var supportedLocale;for(var i=0;i<requested.length;i++){var locale=requested[i];if(available.indexOf(locale)!==-1){supportedLocale=locale;break;}}
|
||||
if(!supportedLocale||supportedLocale===defaultLocale){return[defaultLocale];}
|
||||
return[supportedLocale,defaultLocale];}
|
||||
function freeze(supported){var locale=this.getLocale(supported[0]);if(locale.isReady){setReady.call(this,supported);}else{locale.build(setReady.bind(this,supported));}}
|
||||
function setReady(supported){this.supportedLocales=supported;this.isReady=true;this._emitter.emit('ready');}
|
||||
Context.prototype.registerLocales=function(defLocale,available){if(defLocale){this.defaultLocale=defLocale;}
|
||||
this.availableLocales=[this.defaultLocale];this.qps=Object.keys(PSEUDO);if(available){for(var i=0,loc;loc=available[i];i++){if(this.availableLocales.indexOf(loc)===-1){this.availableLocales.push(loc);var pos=this.qps.indexOf(loc);if(pos!==-1){this.qps.splice(pos,1);}}}}};Context.prototype.requestLocales=function requestLocales(){if(this.isLoading&&!this.isReady){throw new L10nError('Context not ready');}
|
||||
this.isLoading=true;var requested=Array.prototype.slice.call(arguments);if(requested.length===0){throw new L10nError('No locales requested');}
|
||||
var supported=negotiate(this.availableLocales.concat(this.qps),requested,this.defaultLocale);if(this.supportedLocales[0]!==supported[0]){freeze.call(this,supported);}};Context.prototype.addEventListener=function(type,listener){this._emitter.addEventListener(type,listener);};Context.prototype.removeEventListener=function(type,listener){this._emitter.removeEventListener(type,listener);};Context.prototype.ready=function(callback){if(this.isReady){setTimeout(callback);}
|
||||
this.addEventListener('ready',callback);};Context.prototype.once=function(callback){if(this.isReady){setTimeout(callback);return;}
|
||||
var callAndRemove=(function(){this.removeEventListener('ready',callAndRemove);callback();}).bind(this);this.addEventListener('ready',callAndRemove);};var allowed={elements:['a','em','strong','small','s','cite','q','dfn','abbr','data','time','code','var','samp','kbd','sub','sup','i','b','u','mark','ruby','rt','rp','bdi','bdo','span','br','wbr'],attributes:{global:['title','aria-label','aria-valuetext','aria-moz-hint'],a:['download'],area:['download','alt'],input:['alt','placeholder'],menuitem:['label'],menu:['label'],optgroup:['label'],option:['label'],track:['label'],img:['alt'],textarea:['placeholder'],th:['abbr']}};var rtlList=['ar','he','fa','ps','ar-x-psbidi','ur'];var nodeObserver=null;var pendingElements=null;var moConfig={attributes:true,characterData:false,childList:true,subtree:true,attributeFilter:['data-l10n-id','data-l10n-args']};navigator.mozL10n={ctx:null,get:function get(id,ctxdata){return navigator.mozL10n.ctx.get(id,ctxdata);},formatValue:function(id,ctxdata){return navigator.mozL10n.ctx.formatValue(id,ctxdata);},formatEntity:function(id,ctxdata){return navigator.mozL10n.ctx.formatEntity(id,ctxdata);},translateFragment:function(fragment){return translateFragment.call(navigator.mozL10n,fragment);},setAttributes:setL10nAttributes,getAttributes:getL10nAttributes,ready:function ready(callback){return navigator.mozL10n.ctx.ready(callback);},once:function once(callback){return navigator.mozL10n.ctx.once(callback);},get readyState(){return navigator.mozL10n.ctx.isReady?'complete':'loading';},language:{set code(lang){navigator.mozL10n.ctx.requestLocales(lang);},get code(){return navigator.mozL10n.ctx.supportedLocales[0];},get direction(){return getDirection(navigator.mozL10n.ctx.supportedLocales[0]);}},qps:PSEUDO,_config:{appVersion:null,localeSources:Object.create(null),isPretranslated:false,},_getInternalAPI:function(){return{Error:L10nError,Context:Context,Locale:Locale,Resolver:Resolver,getPluralRule:getPluralRule,rePlaceables:rePlaceables,translateDocument:translateDocument,onMetaInjected:onMetaInjected,PropertiesParser:PropertiesParser,walkContent:walkContent,buildLocaleList:buildLocaleList};}};function getDirection(lang){return(rtlList.indexOf(lang)>=0)?'rtl':'ltr';}
|
||||
var readyStates={loading:0,interactive:1,complete:2};function whenInteractive(callback){if(readyStates[document.readyState]>=readyStates.interactive){callback();return;}
|
||||
document.addEventListener('readystatechange',function l10n_onrsc(){if(readyStates[document.readyState]>=readyStates.interactive){document.removeEventListener('readystatechange',l10n_onrsc);callback();}});}
|
||||
function initObserver(){nodeObserver=new MutationObserver(onMutations.bind(navigator.mozL10n));nodeObserver.observe(document,moConfig);}
|
||||
function init(pretranslate){if(!pretranslate){initObserver();}
|
||||
initResources.call(navigator.mozL10n);}
|
||||
function initResources(){var meta={};var nodes=document.head.querySelectorAll('link[rel="localization"],'+'meta[name="availableLanguages"],'+'meta[name="defaultLanguage"],'+'meta[name="appVersion"],'+'script[type="application/l10n"]');for(var i=0,node;node=nodes[i];i++){var type=node.getAttribute('rel')||node.nodeName.toLowerCase();switch(type){case'localization':this.ctx.resLinks.push(node.getAttribute('href'));break;case'meta':onMetaInjected.call(this,node,meta);break;case'script':onScriptInjected.call(this,node);break;}}
|
||||
var additionalLanguagesPromise;if(navigator.mozApps&&navigator.mozApps.getAdditionalLanguages){additionalLanguagesPromise=navigator.mozApps.getAdditionalLanguages().catch(function(e){console.error('Error while loading getAdditionalLanguages',e);});document.addEventListener('additionallanguageschange',function(evt){registerLocales.call(this,meta,evt.detail);this.ctx.requestLocales.apply(this.ctx,navigator.languages||[navigator.language]);}.bind(this));}else{additionalLanguagesPromise=Promise.resolve();}
|
||||
additionalLanguagesPromise.then(function(extraLangs){registerLocales.call(this,meta,extraLangs);initLocale.call(this);}.bind(this));}
|
||||
function registerLocales(meta,extraLangs){var locales=buildLocaleList.call(this,meta,extraLangs);navigator.mozL10n._config.localeSources=locales[1];this.ctx.registerLocales(locales[0],Object.keys(locales[1]));}
|
||||
function getMatchingLangpack(appVersion,langpacks){for(var i=0,langpack;(langpack=langpacks[i]);i++){if(langpack.target===appVersion){return langpack;}}
|
||||
return null;}
|
||||
function buildLocaleList(meta,extraLangs){var loc,lp;var localeSources=Object.create(null);var defaultLocale=meta.defaultLanguage||this.ctx.defaultLocale;if(meta.availableLanguages){for(loc in meta.availableLanguages){localeSources[loc]='app';}}
|
||||
if(extraLangs){for(loc in extraLangs){lp=getMatchingLangpack(this._config.appVersion,extraLangs[loc]);if(!lp){continue;}
|
||||
if(!(loc in localeSources)||!meta.availableLanguages[loc]||parseInt(lp.revision)>meta.availableLanguages[loc]){localeSources[loc]='extra';}}}
|
||||
if(!(defaultLocale in localeSources)){localeSources[defaultLocale]='app';}
|
||||
return[defaultLocale,localeSources];}
|
||||
function splitAvailableLanguagesString(str){var langs={};str.split(',').forEach(function(lang){lang=lang.trim().split(':');langs[lang[0]]=parseInt(lang[1]);});return langs;}
|
||||
function onMetaInjected(node,meta){switch(node.getAttribute('name')){case'availableLanguages':meta.availableLanguages=splitAvailableLanguagesString(node.getAttribute('content'));break;case'defaultLanguage':meta.defaultLanguage=node.getAttribute('content');break;case'appVersion':navigator.mozL10n._config.appVersion=node.getAttribute('content');break;}}
|
||||
function onScriptInjected(node){var lang=node.getAttribute('lang');var locale=this.ctx.getLocale(lang);locale.addAST(JSON.parse(node.textContent));}
|
||||
function initLocale(){this.ctx.requestLocales.apply(this.ctx,navigator.languages||[navigator.language]);window.addEventListener('languagechange',function l10n_langchange(){this.ctx.requestLocales.apply(this.ctx,navigator.languages||[navigator.language]);}.bind(this));}
|
||||
function localizeMutations(mutations){var mutation;var targets=new Set();for(var i=0;i<mutations.length;i++){mutation=mutations[i];if(mutation.type==='childList'){var addedNode;for(var j=0;j<mutation.addedNodes.length;j++){addedNode=mutation.addedNodes[j];if(addedNode.nodeType!==Node.ELEMENT_NODE){continue;}
|
||||
targets.add(addedNode);}}
|
||||
if(mutation.type==='attributes'){targets.add(mutation.target);}}
|
||||
targets.forEach(function(target){if(target.childElementCount){translateFragment.call(this,target);}else if(target.hasAttribute('data-l10n-id')){translateElement.call(this,target);}},this);}
|
||||
function onMutations(mutations,self){self.disconnect();localizeMutations.call(this,mutations);self.observe(document,moConfig);}
|
||||
function onReady(){if(!navigator.mozL10n._config.isPretranslated){translateDocument.call(this);}
|
||||
navigator.mozL10n._config.isPretranslated=false;if(pendingElements){for(var i=0,element;element=pendingElements[i];i++){translateElement.call(this,element);}
|
||||
pendingElements=null;}
|
||||
if(!nodeObserver){initObserver();}
|
||||
fireLocalizedEvent.call(this);}
|
||||
function fireLocalizedEvent(){var event=new CustomEvent('localized',{'bubbles':false,'cancelable':false,'detail':{'language':this.ctx.supportedLocales[0]}});window.dispatchEvent(event);}
|
||||
var reOverlay=/<|&#?\w+;/;var reHtml=/[&<>]/g;var htmlEntities={'&':'&','<':'<','>':'>',};function translateDocument(){document.documentElement.lang=this.language.code;document.documentElement.dir=this.language.direction;translateFragment.call(this,document.documentElement);}
|
||||
function translateFragment(element){if(typeof element.hasAttribute==='function'&&element.hasAttribute('data-l10n-id')){translateElement.call(this,element);}
|
||||
var nodes=getTranslatableChildren(element);for(var i=0;i<nodes.length;i++){translateElement.call(this,nodes[i]);}}
|
||||
function setL10nAttributes(element,id,args){element.setAttribute('data-l10n-id',id);if(args){element.setAttribute('data-l10n-args',JSON.stringify(args));}}
|
||||
function getL10nAttributes(element){return{id:element.getAttribute('data-l10n-id'),args:JSON.parse(element.getAttribute('data-l10n-args'))};}
|
||||
function getTranslatableChildren(element){return element?element.querySelectorAll('*[data-l10n-id]'):[];}
|
||||
function camelCaseToDashed(string){if(string==='ariaValueText'){return'aria-valuetext';}
|
||||
return string.replace(/[A-Z]/g,function(match){return'-'+match.toLowerCase();}).replace(/^-/,'');}
|
||||
function escapeL10nArgs(match){return htmlEntities[match];}
|
||||
function translateElement(element){if(!this.ctx.isReady){if(!pendingElements){pendingElements=[];}
|
||||
pendingElements.push(element);return;}
|
||||
var l10nId=element.getAttribute('data-l10n-id');if(!l10nId){return false;}
|
||||
var l10nArgs=element.getAttribute('data-l10n-args');var entity=this.ctx.getEntity(l10nId,l10nArgs?JSON.parse(l10nArgs.replace(reHtml,escapeL10nArgs)):undefined);var value=entity.value;if(typeof value==='string'){if(!reOverlay.test(value)){element.textContent=value;}else{var translation=element.ownerDocument.createElement('template');translation.innerHTML=value;overlayElement(element,translation.content);}}
|
||||
for(var key in entity.attrs){var attrName=camelCaseToDashed(key);if(isAttrAllowed({name:attrName},element)){element.setAttribute(attrName,entity.attrs[key]);}}}
|
||||
function overlayElement(sourceElement,translationElement){var result=translationElement.ownerDocument.createDocumentFragment();var k,attr;var childElement;while((childElement=translationElement.childNodes[0])){translationElement.removeChild(childElement);if(childElement.nodeType===Node.TEXT_NODE){result.appendChild(childElement);continue;}
|
||||
var index=getIndexOfType(childElement);var sourceChild=getNthElementOfType(sourceElement,childElement,index);if(sourceChild){overlayElement(sourceChild,childElement);result.appendChild(sourceChild);continue;}
|
||||
if(isElementAllowed(childElement)){var sanitizedChild=childElement.ownerDocument.createElement(childElement.nodeName);overlayElement(sanitizedChild,childElement);result.appendChild(sanitizedChild);continue;}
|
||||
result.appendChild(document.createTextNode(childElement.textContent));}
|
||||
sourceElement.textContent='';sourceElement.appendChild(result);if(translationElement.attributes){for(k=0,attr;(attr=translationElement.attributes[k]);k++){if(isAttrAllowed(attr,sourceElement)){sourceElement.setAttribute(attr.name,attr.value);}}}}
|
||||
function isElementAllowed(element){return allowed.elements.indexOf(element.tagName.toLowerCase())!==-1;}
|
||||
function isAttrAllowed(attr,element){var attrName=attr.name.toLowerCase();var tagName=element.tagName.toLowerCase();if(allowed.attributes.global.indexOf(attrName)!==-1){return true;}
|
||||
if(!allowed.attributes[tagName]){return false;}
|
||||
if(allowed.attributes[tagName].indexOf(attrName)!==-1){return true;}
|
||||
if(tagName==='input'&&attrName==='value'){var type=element.type.toLowerCase();if(type==='submit'||type==='button'||type==='reset'){return true;}}
|
||||
return false;}
|
||||
function getNthElementOfType(context,element,index){var nthOfType=0;for(var i=0,child;child=context.children[i];i++){if(child.nodeType===Node.ELEMENT_NODE&&child.tagName===element.tagName){if(nthOfType===index){return child;}
|
||||
nthOfType++;}}
|
||||
return null;}
|
||||
function getIndexOfType(element){var index=0;var child;while((child=element.previousElementSibling)){if(child.tagName===element.tagName){index++;}}
|
||||
return index;}
|
||||
var DEBUG=false;navigator.mozL10n.ctx=new Context(window.document?document.URL:null);navigator.mozL10n.ctx.ready(onReady.bind(navigator.mozL10n));navigator.mozL10n.ctx.addEventListener('notfounderror',function reportMissingEntity(e){if(DEBUG||e.loc==='en-US'){console.warn(e.toString());}});if(DEBUG){navigator.mozL10n.ctx.addEventListener('fetcherror',console.error.bind(console));navigator.mozL10n.ctx.addEventListener('parseerror',console.error.bind(console));navigator.mozL10n.ctx.addEventListener('resolveerror',console.error.bind(console));}
|
||||
if(window.document){navigator.mozL10n._config.isPretranslated=document.documentElement.lang===navigator.language;var forcePretranslate=!navigator.mozL10n._config.isPretranslated;whenInteractive(init.bind(navigator.mozL10n,forcePretranslate));}
|
||||
document.l10n={setAttributes:navigator.mozL10n.setAttributes,getAttributes:navigator.mozL10n.getAttributes,formatValue:function(id,args){return navigator.mozL10n.formatValue(id,args);},translateFragment:function(frag){return Promise.resolve(navigator.mozL10n.translateFragment(frag));},ready:new Promise(function(resolve){navigator.mozL10n.once(resolve);}),formatValues:function(){var keys=arguments;var resp=keys.map(function(key){if(Array.isArray(key)){return navigator.mozL10n.formatValue(key[0],key[1]);}
|
||||
return navigator.mozL10n.formatValue(key);});return Promise.all(resp);},requestLanguages:function(langs){navigator.mozL10n.ctx.requestLocales.apply(navigator.mozL10n.ctx,langs);},pseudo:{'fr-x-psaccent':{getName:function(){return Promise.resolve(navigator.mozL10n.qps['fr-x-psaccent'].name);},processString:function(s){return Promise.resolve(navigator.mozL10n.qps['fr-x-psaccent'].translate(s));}},'ar-x-psbidi':{getName:function(){return Promise.resolve(navigator.mozL10n.qps['ar-x-psbidi'].name);},processString:function(s){return Promise.resolve(navigator.mozL10n.qps['ar-x-psbidi'].translate(s));}}},};navigator.mozL10n.ready(function(){document.documentElement.setAttribute('langs',navigator.mozL10n.ctx.supportedLocales.join(' '));});navigator.mozL10n.once(function(){window.addEventListener('localized',function(){document.dispatchEvent(new CustomEvent('DOMRetranslated',{bubbles:false,cancelable:false}));});});})(this);;;'use strict';var LazyLoader=(function(){function LazyLoader(){this._loaded={};this._isLoading={};}
|
||||
LazyLoader.prototype={_js:function(file,callback){var script=document.createElement('script');script.src=file;script.async=false;script.addEventListener('load',callback);document.head.appendChild(script);this._isLoading[file]=script;},_css:function(file,callback){var style=document.createElement('link');style.type='text/css';style.rel='stylesheet';style.href=file;document.head.appendChild(style);callback();},_html:function(domNode,callback){if(domNode.getAttribute('is')){this.load(['/shared/js/html_imports.js'],function(){HtmlImports.populate(callback);}.bind(this));return;}
|
||||
for(var i=0;i<domNode.childNodes.length;i++){if(domNode.childNodes[i].nodeType==document.COMMENT_NODE){domNode.innerHTML=domNode.childNodes[i].nodeValue;break;}}
|
||||
window.dispatchEvent(new CustomEvent('lazyload',{detail:domNode}));callback();},getJSON:function(file,mozSystem){return new Promise(function(resolve,reject){var xhr;if(mozSystem){xhr=new XMLHttpRequest({mozSystem:true});}else{xhr=new XMLHttpRequest();}
|
||||
xhr.open('GET',file,true);xhr.responseType='json';xhr.onerror=function(error){reject(error);};xhr.onload=function(){if(xhr.response!==null){resolve(xhr.response);}else{reject(new Error('No valid JSON object was found ('+
|
||||
xhr.status+' '+xhr.statusText+')'));}};xhr.send();});},load:function(files,callback){var deferred={};deferred.promise=new Promise(resolve=>{deferred.resolve=resolve;});if(!Array.isArray(files)){files=[files];}
|
||||
var loadsRemaining=files.length,self=this;function perFileCallback(file){if(self._isLoading[file]){delete self._isLoading[file];}
|
||||
self._loaded[file]=true;if(--loadsRemaining===0){deferred.resolve();if(callback){callback();}}}
|
||||
for(var i=0;i<files.length;i++){var file=files[i];if(this._loaded[file.id||file]){perFileCallback(file);}else if(this._isLoading[file]){this._isLoading[file].addEventListener('load',perFileCallback.bind(null,file));}else{var method,idx;if(typeof file==='string'){method=file.match(/\.([^.]+)$/)[1];idx=file;}else{method='html';idx=file.id;}
|
||||
this['_'+method](file,perFileCallback.bind(null,idx));}}
|
||||
return deferred.promise;}};return new LazyLoader();}());;;'use strict';if(!window.ImportStatusData){var ImportStatusData=(function(){var store=null;var DS_NAME='Import_Status_Data';var datastoreLoading=false;var datastoreLoaded=false;var DS_LOADED_EVENT='datastore_loaded';var getDatastore=function getDatastore(){return new Promise(function(resolve,reject){if(!navigator.getDataStores){reject({name:'DatastoreNotEnabled'});return;}
|
||||
if(datastoreLoaded){resolve(store);return;}
|
||||
if(datastoreLoading){document.addEventListener(DS_LOADED_EVENT,function loadedHandler(){document.removeEventListener(DS_LOADED_EVENT,loadedHandler);resolve(store);});}
|
||||
datastoreLoading=true;navigator.getDataStores(DS_NAME).then(function(stores){store=stores[0];},reject).then(function(){datastoreLoading=false;datastoreLoaded=true;document.dispatchEvent(new CustomEvent(DS_LOADED_EVENT));resolve(store);},reject);});};var put=function put(key,obj){return new Promise(function(resolve,reject){getDatastore().then(function success(store){store.put(obj,key).then(resolve,reject);},reject);});};var remove=function remove(key){return new Promise(function(resolve,reject){getDatastore().then(function success(store){store.remove(key).then(resolve,reject);},reject);});};var get=function get(key){return new Promise(function(resolve,reject){getDatastore().then(function success(store){store.get(key).then(resolve,reject);},reject);});};var clear=function clear(){return new Promise(function(resolve,reject){getDatastore().then(function success(store){store.clear().then(resolve,reject);},reject);});};return{put:put,remove:remove,get:get,clear:clear};})();};'use strict';if(typeof window.oauthflow==='undefined'){(function(document){var OAuthFlow=window.oauthflow={};var OAUTH_REDIRECT='redirectURI';var ENDPOINT='loginPage';var APP_ID='applicationId';var SCOPE='scope';var APP_ORIGIN=location.origin;OAuthFlow.init=function(service){var hash=document.location.hash.substring(1);var parameters={};var dataStart=hash.indexOf('access_token');if(dataStart!==-1){var elements=hash.split('&');elements.forEach(function(p){var values=p.split('=');parameters[values[0]]=values[1];});window.opener.postMessage(parameters,APP_ORIGIN);window.close();}};OAuthFlow.start=function(state,service){getAccessToken(state,service);};function getAccessToken(state,service){startOAuth(state,service);}
|
||||
function startOAuth(state,service){var params=oauthflow.params[service];var redirect_uri=encodeURIComponent(params[OAUTH_REDIRECT]);var scope=params[SCOPE].join(',');var scopeParam=encodeURIComponent(scope);var queryParams=['client_id='+params[APP_ID],'redirect_uri='+redirect_uri,'response_type=token','scope='+scopeParam,'approval_prompt=force','state='+state];var query=queryParams.join('&');var url=params[ENDPOINT]+query;window.open(url,'','dialog');}})(document);};'use strict';var fb=window.fb||{};if(typeof window.oauth2==='undefined'){(function(document){var oauth2=window.oauth2={};var accessTokenCbData;var STORAGE_KEY='tokenData';var APP_ORIGIN=location.origin;function clearStorage(service){ImportStatusData.remove(getKey(service));}
|
||||
function getKey(service){var key=STORAGE_KEY;if(service!=='facebook'){key=STORAGE_KEY+'_'+service;}
|
||||
return key;}
|
||||
oauth2.getAccessToken=function(ready,state,service){accessTokenCbData={callback:ready,state:state,service:service};fb.testToken=fb.testToken||(parent.fb&&parent.fb.testToken);if(service==='facebook'&&typeof fb.testToken==='string'&&fb.testToken.trim().length>0){window.console.warn('Facebook. A test token will be used!');tokenDataReady({data:{access_token:fb.testToken,expires_in:0,state:state}});return;}
|
||||
ImportStatusData.get(getKey(service)).then(function getAccessToken(tokenData){if(!tokenData||!tokenData.access_token){startOAuth(state,service);return;}
|
||||
var access_token=tokenData.access_token;var expires=Number(tokenData.expires);var token_ts=tokenData.token_ts;if(expires!==0&&Date.now()-token_ts>=expires){window.console.warn('Access token has expired, restarting flow');startOAuth(state,service);return;}
|
||||
if(typeof ready==='function'){ready(access_token);}});};function startOAuth(state,service){clearStorage(service);oauthflow.start(state,service);}
|
||||
function tokenDataReady(e){var parameters=e.data;if(!parameters||!parameters.access_token){return;}
|
||||
if(e.origin!==APP_ORIGIN){return;}
|
||||
Curtain.show('wait',accessTokenCbData.state);window.setTimeout(function do_token_ready(){var access_token=parameters.access_token;if(parameters.state===accessTokenCbData.state){accessTokenCbData.callback(access_token);}else{window.console.error('FB: Error in state',parameters.state,accessTokenCbData.state);return;}
|
||||
var end=parameters.expires_in;ImportStatusData.put(getKey(accessTokenCbData.service),{access_token:access_token,expires:end*1000,token_ts:Date.now()}).then(function notify_parent(){parent.postMessage({type:'token_stored',data:''},APP_ORIGIN);});},0);}
|
||||
window.addEventListener('message',tokenDataReady);})(document);};var oauthflow=this.oauthflow||{};oauthflow.params={"facebook":{"redirectURI":"https://www.facebook.com/connect/login_success.html","loginPage":"https://m.facebook.com/dialog/oauth/?","applicationId":"123456","scope":["friends_about_me","friends_birthday","friends_hometown","friends_location","friends_work_history","read_stream"],"redirectMsg":"http://intense-tundra-4122.herokuapp.com/fbowd/oauth2_new/dialogs_end.html","redirectLogout":"http://intense-tundra-4122.herokuapp.com/fbowd/oauth2_new/dialogs_end.html","imgDetailWidth":200,"imgThumbSize":120},"live":{"redirectURI":"https://www.mozilla.org/index.html","loginPage":"https://login.live.com/oauth20_authorize.srf?","applicationId":"123456","scope":["wl.basic","wl.contacts_emails","wl.contacts_phone_numbers","wl.contacts_birthday","wl.contacts_postal_addresses"],"logoutUrl":"https://login.live.com/logout.srf"},"gmail":{"redirectURI":"https://serene-cove-3587.herokuapp.com/liveowd/oauth2_new/flow_live.html","loginPage":"https://accounts.google.com/o/oauth2/auth?","applicationId":"664741361278.apps.googleusercontent.com","scope":["https://www.google.com/m8/feeds/"],"logoutUrl":"https://accounts.google.com/Logout"}};;'use strict';var Curtain=(function(){var curtainFrame=parent.document.querySelector('#iframe_curtain');if(!curtainFrame){curtainFrame=document.createElement('iframe');curtainFrame.id='iframe_curtain';curtainFrame.src='/shared/pages/import/curtain.html';parent.document.body.appendChild(curtainFrame);}
|
||||
function getDoc(){return curtainFrame.contentWindow.document;}
|
||||
var cpuWakeLock,cancelButton,retryButton,okButton,progressElement,form,progressTitle;var messages=[];var elements=['error','timeout','wait','message','progress','alert'];if(getDoc().readyState==='complete'){init();}else{curtainFrame.addEventListener('load',function loaded(){curtainFrame.removeEventListener('load',loaded);init();});}
|
||||
function init(){var doc=getDoc();cancelButton=doc.querySelector('#cancel');retryButton=doc.querySelector('#retry');okButton=doc.querySelector('#ok');progressElement=doc.querySelector('#progressElement');form=doc.querySelector('form');elements.forEach(function createElementRef(name){messages[name]=doc.getElementById(name+'Msg');});progressTitle=doc.getElementById('progressTitle');}
|
||||
function doShow(type){form.classList.remove('no-menu');form.dataset.state=type;curtainFrame.classList.add('visible');curtainFrame.classList.remove('fade-out');curtainFrame.classList.add('fade-in');}
|
||||
function capitalize(str){return str.charAt(0).toUpperCase()+str.slice(1);}
|
||||
function Progress(pfrom){var from=pfrom;var counter=0;var total=0;progressElement.setAttribute('value',0);function showMessage(){document.l10n.setAttributes(messages.progress,'progressFB',{current:counter,total:total});}
|
||||
this.update=function(){progressElement.setAttribute('value',(++counter*100)/total);showMessage();};this.setFrom=function(pfrom){from=capitalize(pfrom);progressTitle.setAttribute('data-l10n-id','progressFB3'+from+'Title');};this.setTotal=function(ptotal){total=ptotal;showMessage();};this.getValue=function(){return counter;};}
|
||||
return{show:function(type,from){var out;from=capitalize(from);switch(type){case'wait':messages[type].setAttribute('data-l10n-id',type+from);break;case'timeout':document.l10n.formatValue('timeout'+from).then((from)=>{document.l10n.setAttributes(messages[type],'timeout1',{from:from});});break;case'error':document.l10n.formatValue('type'+from).then((from)=>{document.l10n.setAttributes(messages[type],'error1',{from:from});});break;case'alert':case'message':messages[type].setAttribute('data-l10n-id',type+from);break;case'progress':progressTitle.setAttribute('data-l10n-id',type+'FB3'+from+'Title');out=new Progress(from);cpuWakeLock=navigator.requestWakeLock('cpu');break;}
|
||||
doShow(type);return out;},hide:function c_hide(hiddenCB){if(cpuWakeLock){cpuWakeLock.unlock();cpuWakeLock=null;}
|
||||
curtainFrame.classList.add('fade-out');curtainFrame.addEventListener('animationend',function cu_fadeOut(ev){curtainFrame.removeEventListener('animationend',cu_fadeOut);curtainFrame.classList.remove('visible');curtainFrame.classList.remove('fade-out');curtainFrame.classList.remove('fade-in');delete form.dataset.state;if(typeof hiddenCB==='function'){hiddenCB();}});},set oncancel(cancelCb){if(typeof cancelCb==='function'){delete cancelButton.onclick;cancelButton.onclick=function on_cancel(e){delete cancelButton.onclick;cancelCb();return false;};}},set onretry(retryCb){if(typeof retryCb==='function'){delete retryButton.onclick;retryButton.onclick=function on_retry(e){delete retryButton.onclick;retryCb();return false;};}},set onok(okCb){if(typeof okCb==='function'){delete okButton.onclick;okButton.onclick=function on_ok(e){delete okButton.onclick;okCb();return false;};}},get visible(){return curtainFrame.classList.contains('visible');},hideMenu:function c_hideMenu(){form.classList.add('no-menu');}};})();;'use strict';if(typeof window.oauthFrame==='undefined'){(function(){var targetService;var oauthFrame=window.oauthFrame={};var CONTACTS_APP_ORIGIN=location.origin;function notifyParent(message,origin){parent.postMessage({type:message.type||'',data:message.data||''},origin);}
|
||||
function cancelCb(){Curtain.hide(notifyParent.bind(null,{type:'abort'},CONTACTS_APP_ORIGIN));}
|
||||
oauthFrame.start=function(from,service){targetService=service;oauth2.getAccessToken(function tokenReady(access_token){Curtain.oncancel=cancelCb;if(!Curtain.visible){Curtain.show('wait',from);}
|
||||
parent.postMessage({type:'authenticated',data:access_token},CONTACTS_APP_ORIGIN);},from,service);};window.addEventListener('message',function messageHandler(e){if(e.origin!==CONTACTS_APP_ORIGIN){return;}
|
||||
var data=e.data;if(data&&data.type==='start'){oauthFrame.start(data.data.from,data.data.service);}});})();}
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
|
||||
<meta name="defaultLanguage" content="en-US">
|
||||
<meta name="availableLanguages" content="ar:201601260729, bg:201601260729, bn-BD:201601260729, ca:201601260729, cs:201601260729, da:201601260729, de:201601260729, el:201601260729, en-US:201601260729, es:201601260729, eu:201601260729, fr:201601260729, gl:201601260729, hr:201601260729, hu:201601260729, it:201601260729, ja:201601260729, km:201601260729, ko:201601260729, lt:201601260729, mk:201601260729, ms:201601260729, ne-NP:201601260729, nl:201601260729, pa:201601260729, pl:201601260729, pt-BR:201601260729, ro:201601260729, ru:201601260729, sk:201601260729, sq:201601260729, sr-Cyrl:201601260729, sr-Latn:201601260729, sv-SE:201601260729, tr:201601260729, zh-CN:201601260729, zh-TW:201601260729">
|
||||
<link rel="localization" href="/locales-obj/shared.pages.import.import.{locale}.json">
|
||||
<!-- <script type="text/javascript" src="/shared/js/l10n.js"></script> -->
|
||||
|
||||
<!-- <script type="text/javascript" src="/shared/js/async_storage.js"></script> -->
|
||||
<!-- <script type="text/javascript" src="/shared/js/lazy_loader.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/text_normalizer.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/simple_phone_matcher.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contact_photo_helper.js"></script> -->
|
||||
|
||||
<!-- <script type="text/javascript" src="/shared/pages/import/js/flow.js"></script> -->
|
||||
<!-- <script type="text/javascript" src="/shared/pages/import/js/oauth20.js"></script> -->
|
||||
<!-- <script type="text/javascript" src="/shared/pages/import/js/parameters.js"></script> -->
|
||||
<!-- <script type="text/javascript" src="/shared/pages/import/js/curtain.js"></script> --><script src="./gaia_build_import.js" type="text/javascript"></script>
|
||||
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contacts/contacts_matcher.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contacts/contacts_merger.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contacts/merger_adapter.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contacts/contacts_shortcuts.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contacts/contacts_cleaner.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contacts/utilities/image_square.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contacts/utilities/image_thumbnail.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contacts/utilities/dom.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contacts/utilities/http_rest.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contacts/utilities/templates.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contacts/utilities/event_listeners.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contacts/utilities/image_loader.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contacts/import/contacts_importer.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contacts/import/importer_ui.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contacts/import/import_init.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contacts/import/import_status_data.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contacts/import/utilities/misc.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/component_utils.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/elements/gaia_buttons/script.js"></script> -->
|
||||
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contacts/import/friends_list.js"></script> -->
|
||||
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/utilities.js"></script> -->
|
||||
<!-- <script defer="" type="text/javascript" src="/shared/js/contacts/search.js"></script> -->
|
||||
|
||||
<!-- To include these files on the standard app
|
||||
<link href="/shared/js/binary_search.js">
|
||||
<link href="/shared/js/fb/fb_tel_index.js">
|
||||
<link href="/shared/js/fb/fb_request.js">
|
||||
<link href="/shared/js/fb/fb_data_reader.js">
|
||||
|
||||
<link href="/shared/js/contacts/import/gmail/gmail_connector.js">
|
||||
<link href="/shared/js/contacts/import/live/live_connector.js">
|
||||
<link href="/shared/js/contacts/import/facebook/facebook_connector.js">
|
||||
|
||||
<link href="/shared/js/contacts/import/utilities/config.js">
|
||||
<link href="/shared/js/image_utils.js">
|
||||
-->
|
||||
|
||||
<!--
|
||||
<link href="/shared/pages/import/redirects/redirect.html">
|
||||
<link href="/shared/pages/import/redirects/dialogs_end.html">
|
||||
-->
|
||||
|
||||
<!--
|
||||
<link href="/shared/js/contacts/import/facebook/fb_data.js">
|
||||
<link href="/shared/js/contacts/import/facebook/fb_utils.js">
|
||||
<link href="/shared/js/contacts/import/facebook/fb_contact_utils.js">
|
||||
<link href="/shared/js/contacts/import/facebook/fb_query.js">
|
||||
<link href="/shared/js/contacts/import/facebook/fb_contact.js">
|
||||
-->
|
||||
|
||||
<!-- Building Blocks -->
|
||||
<link href="/shared/style/progress_activity.css" rel="stylesheet">
|
||||
<link href="/shared/style/status.css" rel="stylesheet">
|
||||
<link href="/shared/style/headers.css" rel="stylesheet">
|
||||
<link href="/shared/style/input_areas.css" rel="stylesheet">
|
||||
<link href="/shared/style/switches.css" rel="stylesheet">
|
||||
<link href="/shared/style/lists.css" rel="stylesheet">
|
||||
<link href="/shared/style/confirm.css" rel="stylesheet">
|
||||
<link href="/shared/style/scrolling.css" rel="stylesheet">
|
||||
<link href="/shared/style/animations.css" rel="stylesheet">
|
||||
|
||||
<!-- Web Components -->
|
||||
<!-- <link rel="stylesheet" type="text/css" href="/shared/elements/gaia-icons/gaia-icons.css" /> -->
|
||||
<link rel="stylesheet" type="text/css" href="/shared/elements/gaia-theme/gaia-theme.css">
|
||||
<!-- <script defer="" src="/shared/elements/config.js"></script> -->
|
||||
<!-- <script defer="" src="/shared/elements/gaia-header/dist/gaia-header.js"></script> --><script src="./gaia_build_defer_import.js" defer="" type="text/javascript"></script>
|
||||
|
||||
<!--
|
||||
<link rel="stylesheet" href="/shared/pages/import/style/root.css">
|
||||
<link rel="stylesheet" href="/shared/pages/import/style/facebook.css">
|
||||
-->
|
||||
<link rel="stylesheet" href="/shared/pages/import/style/import.css">
|
||||
<link rel="stylesheet" href="/shared/pages/import/style/search.css">
|
||||
<meta content="2.5" name="appVersion"></head>
|
||||
|
||||
<body role="application" class="theme-communications skin-comms">
|
||||
<section role="region" id="content" class="import">
|
||||
|
||||
<gaia-header id="header" action="close">
|
||||
<h1>Service</h1>
|
||||
<button id="import-action" data-l10n-id="import" disabled="">Import</button>
|
||||
</gaia-header>
|
||||
|
||||
<section id="main">
|
||||
<!-- Contacts List Section -->
|
||||
<section id="mainContent" data-state="selection">
|
||||
<form id="search-container" role="search" class="search">
|
||||
<p>
|
||||
<label for="search" id="search-start">
|
||||
<input name="search" class="textfield" placeholder="Search" data-l10n-id="search-contact" type="search">
|
||||
</label>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<section class="friends-msg">
|
||||
<output id="num-friends" hidden=""></output>
|
||||
<p id="friends-msg"></p>
|
||||
</section>
|
||||
<section id="groups-list" class="unbordered import-list active" data-type="list">
|
||||
<section id="group-#group#" data-template="">
|
||||
<header>#group#</header>
|
||||
<ol id="contacts-list-#group#">
|
||||
<li data-template="" data-uuid="#uid#" data-search="#search#" aria-disabled="false" class="block-item">
|
||||
<label class="pack-checkbox">
|
||||
<input name="#uid#" type="checkbox">
|
||||
<span></span>
|
||||
</label>
|
||||
<aside class="pack-end">
|
||||
<span data-type="img" data-src="#contactPictureUri#"></span>
|
||||
</aside>
|
||||
<p data-order-by="firstName" class="contact-text"><bdi><strong>#givenName#</strong> #familyName#</bdi></p>
|
||||
<p data-order-by="lastName" class="contact-text"><bdi>#givenName# <strong>#familyName#</strong></bdi></p>
|
||||
<p><bdi>#email1#</bdi></p>
|
||||
</li>
|
||||
</ol>
|
||||
</section> <!-- group template content -->
|
||||
</section> <!-- groupsList -->
|
||||
</section> <!-- mainContent -->
|
||||
|
||||
<form role="dialog" data-type="confirm" class="no-overlay">
|
||||
<gaia-buttons id="select-all-wrapper">
|
||||
<button id="deselect-all" class="edit-button" data-l10n-id="deselectAll">Deselect all</button>
|
||||
<button id="select-all" class="edit-button" data-l10n-id="selectAll">Select all</button>
|
||||
</gaia-buttons>
|
||||
</form>
|
||||
|
||||
<nav data-type="scrollbar">
|
||||
<p></p>
|
||||
<ol>
|
||||
<li data-template="" data-anchor="group-#anchor#" data-letter="#anchor#">
|
||||
<a href="#">#anchor#</a>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</section> <!-- main -->
|
||||
|
||||
<section id="search-view" class="view" role="region">
|
||||
<form id="searchview-container" role="search" class="search">
|
||||
<p>
|
||||
<label for="search-contact">
|
||||
<input name="search" class="textfield" placeholder="Search" id="search-contact" data-l10n-id="search-contact" type="search">
|
||||
<button type="reset">Clear</button>
|
||||
</label>
|
||||
</p>
|
||||
<button id="done-search" type="submit" data-l10n-id="done-search">Done</button>
|
||||
</form>
|
||||
<section id="groups-list-search">
|
||||
<ol id="search-list" class="selecting active" data-type="list">
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section id="search-process">
|
||||
<p id="no-result" class="hide" data-l10n-id="noResults">No contacts found</p>
|
||||
<p id="search-progress" class="hidden" role="status">
|
||||
<progress class="small"></progress>
|
||||
</p>
|
||||
</section>
|
||||
</section> <!-- search-view section -->
|
||||
|
||||
</section> <!-- role region -->
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
;;'use strict';var Curtain=(function(){var curtainFrame=parent.document.querySelector('#iframe_curtain');if(!curtainFrame){curtainFrame=document.createElement('iframe');curtainFrame.id='iframe_curtain';curtainFrame.src='/shared/pages/import/curtain.html';parent.document.body.appendChild(curtainFrame);}
|
||||
function getDoc(){return curtainFrame.contentWindow.document;}
|
||||
var cpuWakeLock,cancelButton,retryButton,okButton,progressElement,form,progressTitle;var messages=[];var elements=['error','timeout','wait','message','progress','alert'];if(getDoc().readyState==='complete'){init();}else{curtainFrame.addEventListener('load',function loaded(){curtainFrame.removeEventListener('load',loaded);init();});}
|
||||
function init(){var doc=getDoc();cancelButton=doc.querySelector('#cancel');retryButton=doc.querySelector('#retry');okButton=doc.querySelector('#ok');progressElement=doc.querySelector('#progressElement');form=doc.querySelector('form');elements.forEach(function createElementRef(name){messages[name]=doc.getElementById(name+'Msg');});progressTitle=doc.getElementById('progressTitle');}
|
||||
function doShow(type){form.classList.remove('no-menu');form.dataset.state=type;curtainFrame.classList.add('visible');curtainFrame.classList.remove('fade-out');curtainFrame.classList.add('fade-in');}
|
||||
function capitalize(str){return str.charAt(0).toUpperCase()+str.slice(1);}
|
||||
function Progress(pfrom){var from=pfrom;var counter=0;var total=0;progressElement.setAttribute('value',0);function showMessage(){document.l10n.setAttributes(messages.progress,'progressFB',{current:counter,total:total});}
|
||||
this.update=function(){progressElement.setAttribute('value',(++counter*100)/total);showMessage();};this.setFrom=function(pfrom){from=capitalize(pfrom);progressTitle.setAttribute('data-l10n-id','progressFB3'+from+'Title');};this.setTotal=function(ptotal){total=ptotal;showMessage();};this.getValue=function(){return counter;};}
|
||||
return{show:function(type,from){var out;from=capitalize(from);switch(type){case'wait':messages[type].setAttribute('data-l10n-id',type+from);break;case'timeout':document.l10n.formatValue('timeout'+from).then((from)=>{document.l10n.setAttributes(messages[type],'timeout1',{from:from});});break;case'error':document.l10n.formatValue('type'+from).then((from)=>{document.l10n.setAttributes(messages[type],'error1',{from:from});});break;case'alert':case'message':messages[type].setAttribute('data-l10n-id',type+from);break;case'progress':progressTitle.setAttribute('data-l10n-id',type+'FB3'+from+'Title');out=new Progress(from);cpuWakeLock=navigator.requestWakeLock('cpu');break;}
|
||||
doShow(type);return out;},hide:function c_hide(hiddenCB){if(cpuWakeLock){cpuWakeLock.unlock();cpuWakeLock=null;}
|
||||
curtainFrame.classList.add('fade-out');curtainFrame.addEventListener('animationend',function cu_fadeOut(ev){curtainFrame.removeEventListener('animationend',cu_fadeOut);curtainFrame.classList.remove('visible');curtainFrame.classList.remove('fade-out');curtainFrame.classList.remove('fade-in');delete form.dataset.state;if(typeof hiddenCB==='function'){hiddenCB();}});},set oncancel(cancelCb){if(typeof cancelCb==='function'){delete cancelButton.onclick;cancelButton.onclick=function on_cancel(e){delete cancelButton.onclick;cancelCb();return false;};}},set onretry(retryCb){if(typeof retryCb==='function'){delete retryButton.onclick;retryButton.onclick=function on_retry(e){delete retryButton.onclick;retryCb();return false;};}},set onok(okCb){if(typeof okCb==='function'){delete okButton.onclick;okButton.onclick=function on_ok(e){delete okButton.onclick;okCb();return false;};}},get visible(){return curtainFrame.classList.contains('visible');},hideMenu:function c_hideMenu(){form.classList.add('no-menu');}};})();
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
;;'use strict';if(typeof window.oauthflow==='undefined'){(function(document){var OAuthFlow=window.oauthflow={};var OAUTH_REDIRECT='redirectURI';var ENDPOINT='loginPage';var APP_ID='applicationId';var SCOPE='scope';var APP_ORIGIN=location.origin;OAuthFlow.init=function(service){var hash=document.location.hash.substring(1);var parameters={};var dataStart=hash.indexOf('access_token');if(dataStart!==-1){var elements=hash.split('&');elements.forEach(function(p){var values=p.split('=');parameters[values[0]]=values[1];});window.opener.postMessage(parameters,APP_ORIGIN);window.close();}};OAuthFlow.start=function(state,service){getAccessToken(state,service);};function getAccessToken(state,service){startOAuth(state,service);}
|
||||
function startOAuth(state,service){var params=oauthflow.params[service];var redirect_uri=encodeURIComponent(params[OAUTH_REDIRECT]);var scope=params[SCOPE].join(',');var scopeParam=encodeURIComponent(scope);var queryParams=['client_id='+params[APP_ID],'redirect_uri='+redirect_uri,'response_type=token','scope='+scopeParam,'approval_prompt=force','state='+state];var query=queryParams.join('&');var url=params[ENDPOINT]+query;window.open(url,'','dialog');}})(document);}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
;;'use strict';var fb=window.fb||{};if(typeof window.oauth2==='undefined'){(function(document){var oauth2=window.oauth2={};var accessTokenCbData;var STORAGE_KEY='tokenData';var APP_ORIGIN=location.origin;function clearStorage(service){ImportStatusData.remove(getKey(service));}
|
||||
function getKey(service){var key=STORAGE_KEY;if(service!=='facebook'){key=STORAGE_KEY+'_'+service;}
|
||||
return key;}
|
||||
oauth2.getAccessToken=function(ready,state,service){accessTokenCbData={callback:ready,state:state,service:service};fb.testToken=fb.testToken||(parent.fb&&parent.fb.testToken);if(service==='facebook'&&typeof fb.testToken==='string'&&fb.testToken.trim().length>0){window.console.warn('Facebook. A test token will be used!');tokenDataReady({data:{access_token:fb.testToken,expires_in:0,state:state}});return;}
|
||||
ImportStatusData.get(getKey(service)).then(function getAccessToken(tokenData){if(!tokenData||!tokenData.access_token){startOAuth(state,service);return;}
|
||||
var access_token=tokenData.access_token;var expires=Number(tokenData.expires);var token_ts=tokenData.token_ts;if(expires!==0&&Date.now()-token_ts>=expires){window.console.warn('Access token has expired, restarting flow');startOAuth(state,service);return;}
|
||||
if(typeof ready==='function'){ready(access_token);}});};function startOAuth(state,service){clearStorage(service);oauthflow.start(state,service);}
|
||||
function tokenDataReady(e){var parameters=e.data;if(!parameters||!parameters.access_token){return;}
|
||||
if(e.origin!==APP_ORIGIN){return;}
|
||||
Curtain.show('wait',accessTokenCbData.state);window.setTimeout(function do_token_ready(){var access_token=parameters.access_token;if(parameters.state===accessTokenCbData.state){accessTokenCbData.callback(access_token);}else{window.console.error('FB: Error in state',parameters.state,accessTokenCbData.state);return;}
|
||||
var end=parameters.expires_in;ImportStatusData.put(getKey(accessTokenCbData.service),{access_token:access_token,expires:end*1000,token_ts:Date.now()}).then(function notify_parent(){parent.postMessage({type:'token_stored',data:''},APP_ORIGIN);});},0);}
|
||||
window.addEventListener('message',tokenDataReady);})(document);}
|
||||