UNPKG

@sketchpixy/rubix

Version:
343 lines 86.9 kB
'use strict';var _extends2=require('babel-runtime/helpers/extends');var _extends3=_interopRequireDefault(_extends2);var _create=require('babel-runtime/core-js/object/create');var _create2=_interopRequireDefault(_create);var _typeof2=require('babel-runtime/helpers/typeof');var _typeof3=_interopRequireDefault(_typeof2);var _keys=require('babel-runtime/core-js/object/keys');var _keys2=_interopRequireDefault(_keys);var _react=require('react');var _react2=_interopRequireDefault(_react);var _Dispatcher=require('./Dispatcher');var _Dispatcher2=_interopRequireDefault(_Dispatcher);var _isBrowser=require('./isBrowser');var _isBrowser2=_interopRequireDefault(_isBrowser);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj};}var win=null;if(!(0,_isBrowser2.default)()){win={};}else{win=window;}(function(window,undefined){function define(name,payload){define.modules[name]=payload;};// un-instantiated modules define.modules={};// instantiated modules define.exports={};function normalize(path){var parts=path.split('/');var normalized=[];for(var i=0;i<parts.length;i++){if(parts[i]=='.'){// don't add it to `normalized` }else if(parts[i]=='..'){normalized.pop();}else{normalized.push(parts[i]);}}return normalized.join('/');}function join(a,b){return a?a.trim().replace(/\/*$/,'/')+b.trim():b.trim();}function dirname(path){return path?path.split('/').slice(0,-1).join('/'):null;}function req(leaf,name){name=normalize(join(dirname(leaf),name));if(name in define.exports){return define.exports[name];}if(!(name in define.modules)){throw new Error("Module not defined: "+name);}var module=define.modules[name];if(typeof module=="function"){var exports={};var reply=module(req.bind(null,name),exports,{id:name,uri:""});module=reply!==undefined?reply:exports;}return define.exports[name]=module;};// for the top-level required modules, leaf is null var require=req.bind(null,null);define('l20n',function(require,exports){'use strict';var Context=require('./l20n/context').Context;var Parser=require('./l20n/parser').Parser;var Compiler=require('./l20n/compiler').Compiler;exports.Context=Context;exports.Parser=Parser;exports.Compiler=Compiler;exports.getContext=function L20n_getContext(id){return new Context(id);};});define('l20n/context',function(require,exports){'use strict';var EventEmitter=require('./events').EventEmitter;var Parser=require('./parser').Parser;var Compiler=require('./compiler').Compiler;var RetranslationManager=require('./retranslation').RetranslationManager;// register globals with RetranslationManager require('./platform/globals');var io=require('./platform/io');function Resource(id,parser){var self=this;this.id=id;this.resources=[];this.source=null;this.ast={type:'LOL',body:[]};this.build=build;var _imports_positions=[];// absolute URLs start with a slash or contain a colon (for schema) var reAbsolute=/^\/|:/;function build(nesting,callback,sync){if(nesting>=7){return callback(new ContextError('Too many nested imports.'));}if(self.source){// Bug 908826 - Don't artificially force asynchronicity when only using // addResource // https://bugzilla.mozilla.org/show_bug.cgi?id=908826 return setTimeout(function(){parse();});}else{io.load(self.id,parse,sync);}function parse(err,text){if(err){return callback(err);}else if(text!==undefined){self.source=text;}self.ast=parser.parse(self.source);buildImports();}function buildImports(){var imports=self.ast.body.filter(function(elem,i){if(elem.type==='ImportStatement'){_imports_positions.push(i);return true;}return false;});imports.forEach(function(imp){var uri=relativeToSelf(imp.uri.content);var res=new Resource(uri,parser);self.resources.push(res);});var importsToBuild=self.resources.length;if(importsToBuild===0){return callback();}self.resources.forEach(function(res){res.build(nesting+1,resourceBuilt,sync);});function resourceBuilt(err){if(err){return callback(err);}importsToBuild--;if(importsToBuild===0){flatten();}}}function flatten(){for(var i=self.resources.length-1;i>=0;i--){var pos=_imports_positions[i]||0;Array.prototype.splice.apply(self.ast.body,[pos,1].concat(self.resources[i].ast.body));}callback();}}function relativeToSelf(url){if(self.id===null||reAbsolute.test(url)){return url;}var dirs=self.id.split('/').slice(0,-1).concat(url.split('/')).filter(function(elem){return elem!=='.';});return dirs.join('/');}}function Locale(id,parser,compiler,emitter){this.id=id;this.resources=[];this.entries=null;this.ast={type:'LOL',body:[]};this.isReady=false;this.build=build;this.getEntry=getEntry;this.hasResource=hasResource;var self=this;function build(callback){if(!callback){var sync=true;}var resourcesToBuild=self.resources.length;if(resourcesToBuild===0){throw new ContextError('Locale has no resources');}var resourcesWithErrors=0;self.resources.forEach(function(res){res.build(0,resourceBuilt,sync);});function resourceBuilt(err){if(err){resourcesWithErrors++;emitter.emit(err instanceof ContextError?'error':'warning',err);}resourcesToBuild--;if(resourcesToBuild===0){if(resourcesWithErrors===self.resources.length){// XXX Bug 908780 - Decide what to do when all resources in // a locale are missing or broken // https://bugzilla.mozilla.org/show_bug.cgi?id=908780 emitter.emit('error',new ContextError('Locale has no valid resources'));}flatten();}}function flatten(){self.ast.body=self.resources.reduce(function(prev,curr){return prev.concat(curr.ast.body);},self.ast.body);compile();}function compile(){self.entries=compiler.compile(self.ast);self.isReady=true;if(callback){callback();}}}function getEntry(id){/* jshint validthis: true */if(this.entries.hasOwnProperty(id)){return this.entries[id];}return undefined;}function hasResource(uri){/* jshint validthis: true */return this.resources.some(function(res){return res.id===uri;});}}function Context(id){this.id=id;this.registerLocales=registerLocales;this.registerLocaleNegotiator=registerLocaleNegotiator;this.requestLocales=requestLocales;this.addResource=addResource;this.linkResource=linkResource;this.updateData=updateData;this.getSync=getSync;this.getEntitySync=getEntitySync;this.localize=localize;this.ready=ready;this.addEventListener=addEvent;this.removeEventListener=removeEvent;Object.defineProperty(this,'supportedLocales',{get:function get(){return _fallbackChain.slice();},enumerable:true});var _data={};// language negotiator function var _negotiator;// registered and available languages var _default='i-default';var _registered=[_default];var _requested=[];var _fallbackChain=[];// Locale objects corresponding to the registered languages var _locales={};// URLs or text of resources (with information about the type) added via // linkResource and addResource var _reslinks=[];var _isReady=false;var _isFrozen=false;var _emitter=new EventEmitter();var _parser=new Parser();var _compiler=new Compiler();var _retr=new RetranslationManager();var self=this;_parser.addEventListener('error',error);_compiler.addEventListener('error',warn);_compiler.setGlobals(_retr.globals);function extend(dst,src){(0,_keys2.default)(src).forEach(function(key){if(src[key]===undefined){// un-define (remove) the property from dst delete dst[key];}else if((0,_typeof3.default)(src[key])!=='object'){// if the source property is a primitive, just copy it overwriting // whatever the destination property is dst[key]=src[key];}else{// if the source property is an object, deep-copy it recursively if((0,_typeof3.default)(dst[key])!=='object'){dst[key]={};}extend(dst[key],src[key]);}});}function updateData(obj){if(!obj||(typeof obj==='undefined'?'undefined':(0,_typeof3.default)(obj))!=='object'){throw new ContextError('Context data must be a non-null object');}extend(_data,obj);}function getSync(id,data){if(!_isReady){throw new ContextError('Context not ready');}return getFromLocale.call(self,0,id,data).value;}function getEntitySync(id,data){if(!_isReady){throw new ContextError('Context not ready');}return getFromLocale.call(self,0,id,data);}function localize(ids,callback){if(!callback){throw new ContextError('No callback passed');}return bindLocalize.call(self,ids,callback);}function ready(callback){if(_isReady){setTimeout(callback);}addEvent('ready',callback);}function bindLocalize(ids,callback,reason){/* jshint validthis: true */var bound={// stop: fn extend:function extend(newIds){for(var i=0;i<newIds.length;i++){if(ids.indexOf(newIds[i])===-1){ids.push(newIds[i]);}}if(!_isReady){return;}var newMany=getMany.call(this,newIds);// rebind the callback in `_retr`: append new globals seen used in // `newIds` and overwrite the callback with a new one which has the // updated `ids` _retr.bindGet({id:callback,callback:bindLocalize.bind(this,ids,callback),globals:(0,_keys2.default)(newMany.globalsUsed)},true);return newMany;}.bind(this),stop:function stop(){_retr.unbindGet(callback);}.bind(this)};// if the ctx isn't ready, bind the callback and return if(!_isReady){_retr.bindGet({id:callback,callback:bindLocalize.bind(this,ids,callback),globals:[]});return bound;}// if the ctx is ready, retrieve the entities var many=getMany.call(this,ids);var l10n={entities:many.entities,// `reason` might be undefined if context was ready before `localize` // was called; in that case, we pass `locales` so that this scenario // is transparent for the callback reason:reason||{locales:_fallbackChain.slice()},stop:function stop(){_retr.unbindGet(callback);}};_retr.bindGet({id:callback,callback:bindLocalize.bind(this,ids,callback),globals:(0,_keys2.default)(many.globalsUsed)});// callback may call bound.extend which will rebind it if needed; for // this to work it needs to be called after _retr.bindGet above; // otherwise bindGet would listen to globals passed initially in // many.globalsUsed callback(l10n);return bound;}function getMany(ids){/* jshint validthis: true */var many={entities:{},globalsUsed:{}};for(var i=0,id;id=ids[i];i++){many.entities[id]=getEntitySync.call(this,id);for(var global in many.entities[id].globals){if(many.entities[id].globals.hasOwnProperty(global)){many.globalsUsed[global]=true;}}}return many;}function getFromLocale(cur,id,data,prevSource){/* jshint validthis: true */var loc=_fallbackChain[cur];if(!loc){error(new RuntimeError('Unable to get translation',id,_fallbackChain));// imitate the return value of Compiler.Entity.get return{value:prevSource?prevSource.source:id,attributes:{},globals:{},locale:prevSource?prevSource.loc:null};}var locale=getLocale(loc);if(!locale.isReady){// build without a callback, synchronously locale.build(null);}var entry=locale.getEntry(id);// if the entry is missing, just go to the next locale immediately if(entry===undefined){warn(new TranslationError('Not found',id,_fallbackChain,locale));return getFromLocale.call(this,cur+1,id,data,prevSource);}// otherwise, try to get the value of the entry var value;try{value=entry.get(getArgs.call(this,data));}catch(e){if(e instanceof Compiler.RuntimeError){error(new TranslationError(e.message,id,_fallbackChain,locale));if(e instanceof Compiler.ValueError){// salvage the source string which the compiler wasn't able to // evaluate completely; this is still better than returning the // identifer; prefer a source string from locales earlier in the // fallback chain, if available var source=prevSource||{source:e.source,loc:locale.id};return getFromLocale.call(this,cur+1,id,data,source);}return getFromLocale.call(this,cur+1,id,data,prevSource);}else{throw error(e);}}value.locale=locale.id;return value;}function getArgs(extra){if(!extra){return _data;}var args={};// deep-clone _data first extend(args,_data);// overwrite args with the extra args passed to the `get` call extend(args,extra);return args;}function addResource(text){if(_isFrozen){throw new ContextError('Context is frozen');}_reslinks.push(['text',text]);}function add(text,locale){var res=new Resource(null,_parser);res.source=text;locale.resources.push(res);}function linkResource(uri){if(_isFrozen){throw new ContextError('Context is frozen');}_reslinks.push([typeof uri==='function'?'template':'uri',uri]);}function link(uri,locale){if(!locale.hasResource(uri)){var res=new Resource(uri,_parser);locale.resources.push(res);}}function registerLocales(defLocale,available){if(_isFrozen){throw new ContextError('Context is frozen');}if(defLocale===undefined){return;}_default=defLocale;_registered=[];if(!available){available=[];}available.push(defLocale);// uniquify `available` into `_registered` available.forEach(function(loc){if(typeof loc!=='string'){throw new ContextError('Language codes must be strings');}if(_registered.indexOf(loc)===-1){_registered.push(loc);}});}function registerLocaleNegotiator(negotiator){if(_isFrozen){throw new ContextError('Context is frozen');}_negotiator=negotiator;}function getLocale(loc){if(_locales[loc]){return _locales[loc];}var locale=new Locale(loc,_parser,_compiler,_emitter);_locales[loc]=locale;// populate the locale with resources for(var j=0;j<_reslinks.length;j++){var res=_reslinks[j];if(res[0]==='text'){// a resource added via addResource(String) add(res[1],locale);}else if(res[0]==='uri'){// a resource added via linkResource(String) link(res[1],locale);}else{// a resource added via linkResource(Function); the function // passed is a URL template and it takes the current locale's code // as an argument link(res[1](locale.id),locale);}}return locale;}function requestLocales(){if(_isFrozen&&!_isReady){throw new ContextError('Context not ready');}if(_reslinks.length===0){warn(new ContextError('Context has no resources; not freezing'));return;}_isFrozen=true;_requested=Array.prototype.slice.call(arguments);if(_requested.length){_requested.forEach(function(loc){if(typeof loc!=='string'){throw new ContextError('Language codes must be strings');}});}// the whole language negotiation process can be asynchronous; for now // we just use _registered as the list of all available locales, but in // the future we might asynchronously try to query a language pack // service of sorts for its own list of locales supported for this // context if(!_negotiator){var Intl=require('./intl').Intl;_negotiator=Intl.prioritizeLocales;}var fallbackChain=_negotiator(_registered,_requested,_default,freeze);// if the negotiator returned something, freeze synchronously if(fallbackChain){freeze(fallbackChain);}}function freeze(fallbackChain){_fallbackChain=fallbackChain;var locale=getLocale(_fallbackChain[0]);if(locale.isReady){setReady();}else{locale.build(setReady);}}function setReady(){_isReady=true;_retr.all(_fallbackChain.slice());_emitter.emit('ready');}function addEvent(type,listener){_emitter.addEventListener(type,listener);}function removeEvent(type,listener){_emitter.removeEventListener(type,listener);}function warn(e){_emitter.emit('warning',e);return e;}function error(e){_emitter.emit('error',e);return e;}}Context.Error=ContextError;Context.RuntimeError=RuntimeError;Context.TranslationError=TranslationError;function ContextError(message){this.name='ContextError';this.message=message;}ContextError.prototype=(0,_create2.default)(Error.prototype);ContextError.prototype.constructor=ContextError;function RuntimeError(message,id,supported){ContextError.call(this,message);this.name='RuntimeError';this.entity=id;this.supportedLocales=supported.slice();this.message=id+': '+message+'; tried '+supported.join(', ');}RuntimeError.prototype=(0,_create2.default)(ContextError.prototype);RuntimeError.prototype.constructor=RuntimeError;function TranslationError(message,id,supported,locale){RuntimeError.call(this,message,id,supported);this.name='TranslationError';this.locale=locale.id;this.message='['+this.locale+'] '+id+': '+message;}TranslationError.prototype=(0,_create2.default)(RuntimeError.prototype);TranslationError.prototype.constructor=TranslationError;exports.Context=Context;});define('l20n/events',function(require,exports){'use strict';function EventEmitter(){this._listeners={};}EventEmitter.prototype.emit=function ee_emit(){var args=Array.prototype.slice.call(arguments);var type=args.shift();if(!this._listeners[type]){return false;}var typeListeners=this._listeners[type].slice();for(var i=0;i<typeListeners.length;i++){typeListeners[i].apply(this,args);}return true;};EventEmitter.prototype.addEventListener=function ee_add(type,listener){if(!this._listeners[type]){this._listeners[type]=[];}this._listeners[type].push(listener);return this;};EventEmitter.prototype.removeEventListener=function ee_rm(type,listener){var typeListeners=this._listeners[type];var pos=typeListeners.indexOf(listener);if(pos===-1){return this;}typeListeners.splice(pos,1);return this;};exports.EventEmitter=EventEmitter;});define('l20n/parser',function(require,exports){'use strict';var EventEmitter=require('./events').EventEmitter;function Parser(throwOnErrors){/* Public */this.parse=parse;this.addEventListener=addEvent;this.removeEventListener=removeEvent;/* Private */var MAX_PLACEABLES=100;var _source,_index,_length,_emitter;var getLOL;if(throwOnErrors){getLOL=getLOLPlain;}else{_emitter=new EventEmitter();getLOL=getLOLWithRecover;}function getComment(){_index+=2;var start=_index;var end=_source.indexOf('*/',start);if(end===-1){throw error('Comment without closing tag');}_index=end+2;return{type:'Comment',content:_source.slice(start,end)};}function getAttributes(){var attrs=[];var attr,ws1,ch;while(true){attr=getKVPWithIndex('Attribute');attr.local=attr.key.name.charAt(0)==='_';attrs.push(attr);ws1=getRequiredWS();ch=_source.charAt(_index);if(ch==='>'){break;}else if(!ws1){throw error('Expected ">"');}}return attrs;}function getKVP(type){var key=getIdentifier();getWS();if(_source.charAt(_index)!==':'){throw error('Expected ":"');}++_index;getWS();return{type:type,key:key,value:getValue()};}function getKVPWithIndex(type){var key=getIdentifier();var index=[];if(_source.charAt(_index)==='['){++_index;getWS();index=getItemList(getExpression,']');}getWS();if(_source.charAt(_index)!==':'){throw error('Expected ":"');}++_index;getWS();return{type:type,key:key,value:getValue(),index:index};}function getHash(){++_index;getWS();var defItem,hi,comma,hash=[];var hasDefItem=false;while(true){defItem=false;if(_source.charAt(_index)==='*'){++_index;if(hasDefItem){throw error('Default item redefinition forbidden');}defItem=true;hasDefItem=true;}hi=getKVP('HashItem');hi['default']=defItem;hash.push(hi);getWS();comma=_source.charAt(_index)===',';if(comma){++_index;getWS();}if(_source.charAt(_index)==='}'){++_index;break;}if(!comma){throw error('Expected "}"');}}return{type:'Hash',content:hash};}function _unescapeString(){var ch=_source.charAt(++_index);var cc;if(ch==='u'){// special case for unicode var ucode='';for(var i=0;i<4;i++){ch=_source[++_index];cc=ch.charCodeAt(0);if(cc>96&&cc<103||// a-f cc>64&&cc<71||// A-F cc>47&&cc<58){// 0-9 ucode+=ch;}else{throw error('Illegal unicode escape sequence');}}return String.fromCharCode(parseInt(ucode,16));}return ch;}function getComplexString(opchar,opcharLen){var body=null;var buf='';var placeables=0;var ch;_index+=opcharLen-1;var start=_index+1;walkChars:while(true){ch=_source.charAt(++_index);switch(ch){case'\\':buf+=_unescapeString();break;case'{':/* We want to go to default unless {{ *//* jshint -W086 */if(_source.charAt(_index+1)==='{'){if(body===null){body=[];}if(placeables>MAX_PLACEABLES-1){throw error('Too many placeables, maximum allowed is '+MAX_PLACEABLES);}if(buf){body.push({type:'String',content:buf});}_index+=2;getWS();body.push(getExpression());getWS();if(_source.charAt(_index)!=='}'||_source.charAt(_index+1)!=='}'){throw error('Expected "}}"');}_index+=1;placeables++;buf='';break;}default:if(opcharLen===1){if(ch===opchar){_index++;break walkChars;}}else{if(ch===opchar[0]&&_source.charAt(_index+1)===ch&&_source.charAt(_index+2)===ch){_index+=3;break walkChars;}}buf+=ch;if(_index+1>=_length){throw error('Unclosed string literal');}}}if(body===null){return{type:'String',content:buf};}if(buf.length){body.push({type:'String',content:buf});}return{type:'ComplexString',content:body,source:_source.slice(start,_index-opcharLen)};}function getString(opchar,opcharLen){var opcharPos=_source.indexOf(opchar,_index+opcharLen);var placeablePos,escPos,buf;if(opcharPos===-1){throw error('Unclosed string literal');}buf=_source.slice(_index+opcharLen,opcharPos);placeablePos=buf.indexOf('{{');if(placeablePos!==-1){return getComplexString(opchar,opcharLen);}else{escPos=buf.indexOf('\\');if(escPos!==-1){return getComplexString(opchar,opcharLen);}}_index=opcharPos+opcharLen;return{type:'String',content:buf};}function getValue(optional,ch){if(ch===undefined){ch=_source.charAt(_index);}if(ch==='\''||ch==='"'){if(ch===_source.charAt(_index+1)&&ch===_source.charAt(_index+2)){return getString(ch+ch+ch,3);}return getString(ch,1);}if(ch==='{'){return getHash();}if(!optional){throw error('Unknown value type');}return null;}function getRequiredWS(){var pos=_index;var cc=_source.charCodeAt(pos);// space, \n, \t, \r while(cc===32||cc===10||cc===9||cc===13){cc=_source.charCodeAt(++_index);}return _index!==pos;}function getWS(){var cc=_source.charCodeAt(_index);// space, \n, \t, \r while(cc===32||cc===10||cc===9||cc===13){cc=_source.charCodeAt(++_index);}}function getVariable(){++_index;return{type:'VariableExpression',id:getIdentifier()};}function getIdentifier(){var index=_index;var start=index;var source=_source;var cc=source.charCodeAt(start);// a-zA-Z_ if((cc<97||cc>122)&&(cc<65||cc>90)&&cc!==95){throw error('Identifier has to start with [a-zA-Z_]');}cc=source.charCodeAt(++index);while(cc>=97&&cc<=122||// a-z cc>=65&&cc<=90||// A-Z cc>=48&&cc<=57||// 0-9 cc===95){// _ cc=source.charCodeAt(++index);}_index=index;return{type:'Identifier',name:source.slice(start,index)};}function getImportStatement(){_index+=6;if(_source.charAt(_index)!=='('){throw error('Expected "("');}++_index;getWS();var uri=getString(_source.charAt(_index),1);getWS();if(_source.charAt(_index)!==')'){throw error('Expected ")"');}++_index;return{type:'ImportStatement',uri:uri};}function getMacro(id){if(id.name.charAt(0)==='_'){throw error('Macro ID cannot start with "_"');}++_index;var idlist=getItemList(getVariable,')');getRequiredWS();if(_source.charAt(_index)!=='{'){throw error('Expected "{"');}++_index;getWS();var exp=getExpression();getWS();if(_source.charAt(_index)!=='}'){throw error('Expected "}"');}++_index;getWS();if(_source.charCodeAt(_index)!==62){throw error('Expected ">"');}++_index;return{type:'Macro',id:id,args:idlist,expression:exp};}function getEntity(id,index){if(!getRequiredWS()){throw error('Expected white space');}var ch=_source.charAt(_index);var value=getValue(true,ch);var attrs=null;if(value===null){if(ch==='>'){throw error('Expected ">"');}attrs=getAttributes();}else{var ws1=getRequiredWS();if(_source.charAt(_index)!=='>'){if(!ws1){throw error('Expected ">"');}attrs=getAttributes();}}// skip '>' ++_index;return{type:'Entity',id:id,value:value,index:index,attrs:attrs,local:id.name.charCodeAt(0)===95// _ };}function getEntry(){var cc=_source.charCodeAt(_index);// 60 == '<' if(cc===60){++_index;var id=getIdentifier();cc=_source.charCodeAt(_index);// 40 == '(' if(cc===40){return getMacro(id);}// 91 == '[' if(cc===91){++_index;return getEntity(id,getItemList(getExpression,']'));}return getEntity(id,null);}// 47, 42 == '/*' if(_source.charCodeAt(_index)===47&&_source.charCodeAt(_index+1)===42){return getComment();}if(_source.slice(_index,_index+6)==='import'){return getImportStatement();}throw error('Invalid entry');}function getLOLWithRecover(){var entries=[];getWS();while(_index<_length){try{entries.push(getEntry());}catch(e){if(e instanceof ParserError){_emitter.emit('error',e);entries.push(recover());}else{throw e;}}if(_index<_length){getWS();}}return{type:'LOL',body:entries};}function getLOLPlain(){var entries=[];getWS();while(_index<_length){entries.push(getEntry());if(_index<_length){getWS();}}return{type:'LOL',body:entries};}/* Public API functions */function parse(string){_source=string;_index=0;_length=_source.length;return getLOL();}function addEvent(type,listener){if(!_emitter){throw new Error('Emitter not available');}return _emitter.addEventListener(type,listener);}function removeEvent(type,listener){if(!_emitter){throw new Error('Emitter not available');}return _emitter.removeEventListener(type,listener);}/* Expressions */function getExpression(){return getConditionalExpression();}function getPrefixExpression(token,cl,op,nxt){var exp=nxt();var t,ch;while(true){t='';getWS();ch=_source.charAt(_index);if(token[0].indexOf(ch)===-1){break;}t+=ch;++_index;if(token.length>1){ch=_source.charAt(_index);if(token[1]===ch){++_index;t+=ch;}else if(token[2]){--_index;return exp;}}getWS();exp={type:cl,operator:{type:op,token:t},left:exp,right:nxt()};}return exp;}function getPostfixExpression(token,cl,op,nxt){var cc=_source.charCodeAt(_index);if(token.indexOf(cc)===-1){return nxt();}++_index;getWS();return{type:cl,operator:{type:op,token:String.fromCharCode(cc)},argument:getPostfixExpression(token,cl,op,nxt)};}function getConditionalExpression(){var exp=getOrExpression();getWS();if(_source.charCodeAt(_index)!==63){// ? return exp;}++_index;getWS();var consequent=getExpression();getWS();if(_source.charCodeAt(_index)!==58){// : throw error('Expected ":"');}++_index;getWS();return{type:'ConditionalExpression',test:exp,consequent:consequent,alternate:getExpression()};}function getOrExpression(){return getPrefixExpression([['|'],'|',true],'LogicalExpression','LogicalOperator',getAndExpression);}function getAndExpression(){return getPrefixExpression([['&'],'&',true],'LogicalExpression','Logicalperator',getEqualityExpression);}function getEqualityExpression(){return getPrefixExpression([['=','!'],'=',true],'BinaryExpression','BinaryOperator',getRelationalExpression);}function getRelationalExpression(){return getPrefixExpression([['<','>'],'=',false],'BinaryExpression','BinaryOperator',getAdditiveExpression);}function getAdditiveExpression(){return getPrefixExpression([['+','-']],'BinaryExpression','BinaryOperator',getModuloExpression);}function getModuloExpression(){return getPrefixExpression([['%']],'BinaryExpression','BinaryOperator',getMultiplicativeExpression);}function getMultiplicativeExpression(){return getPrefixExpression([['*']],'BinaryExpression','BinaryOperator',getDividiveExpression);}function getDividiveExpression(){return getPrefixExpression([['/']],'BinaryExpression','BinaryOperator',getUnaryExpression);}function getUnaryExpression(){return getPostfixExpression([43,45,33],// + - ! 'UnaryExpression','UnaryOperator',getMemberExpression);}function getCallExpression(callee){getWS();return{type:'CallExpression',callee:callee,arguments:getItemList(getExpression,')')};}function getAttributeExpression(idref,computed){if(idref.type!=='ParenthesisExpression'&&idref.type!=='Identifier'&&idref.type!=='ThisExpression'){throw error('AttributeExpression must have Identifier, This or '+'Parenthesis as left node');}var exp;if(computed){getWS();exp=getExpression();getWS();if(_source.charAt(_index)!==']'){throw error('Expected "]"');}++_index;return{type:'AttributeExpression',expression:idref,attribute:exp,computed:true};}exp=getIdentifier();return{type:'AttributeExpression',expression:idref,attribute:exp,computed:false};}function getPropertyExpression(idref,computed){var exp;if(computed){getWS();exp=getExpression();getWS();if(_source.charAt(_index)!==']'){throw error('Expected "]"');}++_index;return{type:'PropertyExpression',expression:idref,property:exp,computed:true};}exp=getIdentifier();return{type:'PropertyExpression',expression:idref,property:exp,computed:false};}function getMemberExpression(){var exp=getParenthesisExpression();var cc;// 46: '.' // 40: '(' // 58: ':' // 91: '[' while(true){cc=_source.charCodeAt(_index);if(cc===46||cc===91){// . or [ ++_index;exp=getPropertyExpression(exp,cc===91);}else if(cc===58&&_source.charCodeAt(_index+1)===58){// :: _index+=2;if(_source.charCodeAt(_index)===91){// [ ++_index;exp=getAttributeExpression(exp,true);}else{exp=getAttributeExpression(exp,false);}}else if(cc===40){// ( ++_index;exp=getCallExpression(exp);}else{break;}}return exp;}function getParenthesisExpression(){// 40 == ( if(_source.charCodeAt(_index)===40){++_index;getWS();var pexp={type:'ParenthesisExpression',expression:getExpression()};getWS();if(_source.charCodeAt(_index)!==41){throw error('Expected ")"');}++_index;return pexp;}return getPrimaryExpression();}function getPrimaryExpression(){var pos=_index;var cc=_source.charCodeAt(pos);// number while(cc>47&&cc<58){cc=_source.charCodeAt(++pos);}if(pos>_index){var start=_index;_index=pos;return{type:'Number',value:parseInt(_source.slice(start,pos),10)};}switch(cc){// value: '"{[ case 39:case 34:case 123:case 91:return getValue();// variable: $ case 36:return getVariable();// globals: @ case 64:++_index;return{type:'GlobalsExpression',id:getIdentifier()};// this: ~ case 126:++_index;return{type:'ThisExpression'};default:return getIdentifier();}}/* helper functions */function getItemList(callback,closeChar){var ch;getWS();if(_source.charAt(_index)===closeChar){++_index;return[];}var items=[];while(true){items.push(callback());getWS();ch=_source.charAt(_index);if(ch===','){++_index;getWS();}else if(ch===closeChar){++_index;break;}else{throw error('Expected "," or "'+closeChar+'"');}}return items;}function error(message,pos){if(pos===undefined){pos=_index;}var start=_source.lastIndexOf('<',pos-1);var lastClose=_source.lastIndexOf('>',pos-1);start=lastClose>start?lastClose+1:start;var context=_source.slice(start,pos+10);var msg=message+' at pos '+pos+': "'+context+'"';return new ParserError(msg,pos,context);}// This code is being called whenever we // hit ParserError. // // The strategy here is to find the closest entry opening // and skip forward to it. // // It may happen that the entry opening is in fact part of expression, // but this should just trigger another ParserError on the next char // and we'll have to scan for entry opening again until we're successful // or we run out of entry openings in the code. function recover(){var opening=_source.indexOf('<',_index);var junk;if(opening===-1){junk={'type':'JunkEntry','content':_source.slice(_index)};_index=_length;return junk;}junk={'type':'JunkEntry','content':_source.slice(_index,opening)};_index=opening;return junk;}}/* ParserError class */Parser.Error=ParserError;function ParserError(message,pos,context){this.name='ParserError';this.message=message;this.pos=pos;this.context=context;}ParserError.prototype=(0,_create2.default)(Error.prototype);ParserError.prototype.constructor=ParserError;exports.Parser=Parser;});// This is L20n's on-the-fly compiler. It takes the AST produced by the parser // and uses it to create a set of JavaScript objects and functions representing // entities and macros and other expressions. // // The module defines a `Compiler` singleton with a single method: `compile`. // The result of the compilation is stored on the `entries` object passed as // the second argument to the `compile` function. The third argument is // `globals`, an object whose properties provide information about the runtime // environment, e.g., the current hour, operating system etc. // // Main concepts // ------------- // // **Entities** and **attributes** are objects which are publicly available. // Their `toString` method is designed to be used by the L20n context to get // a string value of the entity, given the context data passed to the method. // // All other symbols defined by the grammar are implemented as expression // functions. The naming convention is: // // - capitalized first letters denote **expressions constructors**, e.g. // `PropertyExpression`. // - camel-case denotes **expression functions** returned by the // constructors, e.g. `propertyExpression`. // // ### Constructors // // The constructor is called for every node in the AST. It stores the // components of the expression which are constant and do not depend on the // calling context (an example of the latter would be the data passed by the // developer to the `toString` method). // // ### Expression functions // // The constructor, when called, returns an expression function, which, in // turn, is called every time the expression needs to be evaluated. The // evaluation call is context-dependend. Every expression function takes two // mandatory arguments and one optional one: // // - `locals`, which stores the information about the currently evaluated // entity (`locals.__this__`). It also stores the arguments passed to macros. // - `ctxdata`, which is an object with data passed to the context by the // developer. The developer can define data on the context, or pass it on // a per-call basis. // - `key` (optional), which is a number or a string passed to a `HashLiteral` // expression denoting the member of the hash to return. The member will be // another expression function which can then be evaluated further. // // // Bubbling up the new _current_ entity // ------------------------------------ // // Every expression function returns an array [`newLocals`, `evaluatedValue`]. // The reason for this, and in particular for returning `newLocals`, is // important for understanding how the compiler works. // // In most of the cases. `newLocals` will be the same as the original `locals` // passed to the expression function during the evaluation call. In some // cases, however, `newLocals.__this__` will reference a different entity than // `locals.__this__` did. On runtime, as the compiler traverses the AST and // goes deeper into individual branches, when it hits an `identifier` and // evaluates it to an entity, it needs to **bubble up** this find back to the // top expressions in the chain. This is so that the evaluation of the // top-most expressions in the branch (root being at the very top of the tree) // takes into account the new value of `__this__`. // // To illustrate this point, consider the following example. // // Two entities, `brandName` and `about` are defined as such: // // <brandName { // short: "Firefox", // long: "Mozilla {{ ~ }}" // }> // <about "About {{ brandName.long }}"> // // Notice two `complexString`s: `about` references `brandName.long`, and // `brandName.long` references its own entity via `~`. This `~` (meaning, the // current entity) must always reference `brandName`, even when called from // `about`. // // The AST for the `about` entity looks like this: // // [Entity] // .id[Identifier] // .name[unicode "about"] // .index // .value[ComplexString] <1> // .content // [String] <2> // .content[unicode "About "] // [PropertyExpression] <3> // .expression[Identifier] <4> // .name[unicode "brandName"] // .property[Identifier] // .name[unicode "long"] // .computed[bool=False] // .attrs // .local[bool=False] // // During the compilation the compiler will walk the AST top-down to the // deepest terminal leaves and will use expression constructors to create // expression functions for the components. For instance, for `about`'s value, // the compiler will call `ComplexString()` to create an expression function // `complexString` <1> which will be assigned to the entity's value. The // `ComplexString` construtor, before it returns the `complexString` <1>, will // in turn call other expression constructors to create `content`: // a `stringLiteral` and a `propertyExpression`. The `PropertyExpression` // contructor will do the same, etc... // // When `entity.getString(ctxdata)` is called by a third-party code, we need to // resolve the whole `complexString` <1> to return a single string value. This // is what **resolving** means and it involves some recursion. On the other // hand, **evaluating** means _to call the expression once and use what it // returns_. // // The identifier expression sets `locals.__this__` to the current entity, // `about`, and tells the `complexString` <1> to _resolve_ itself. // // In order to resolve the `complexString` <1>, we start by resolving its first // member <2> to a string. As we resolve deeper down, we bubble down `locals` // set by `toString`. The first member of `content` turns out to simply be // a string that reads `About `. // // On to the second member, the propertyExpression <3>. We bubble down // `locals` again and proceed to evaluate the `expression` field, which is an // `identifier`. Note that we don't _resolve_ it to a string; we _evaluate_ it // to something that can be further used in other expressions, in this case, an // **entity** called `brandName`. // // Had we _resolved_ the `propertyExpression`, it would have resolve to // a string, and it would have been impossible to access the `long` member. // This leads us to an important concept: the compiler _resolves_ expressions // when it expects a primitive value (a string, a number, a bool). On the // other hand, it _evaluates_ expressions (calls them only once) when it needs // to work with them further, e.g. in order to access a member of the hash. // // This also explains why in the above example, once the compiler hits the // `brandName` identifier and changes the value of `locals.__this__` to the // `brandName` entity, this value doesn't bubble up all the way up to the // `about` entity. All components of any `complexString` are _resolved_ by the // compiler until a primitive value is returned. This logic lives in the // `_resolve` function. define('l20n/compiler',function(require,exports){// TODO change newcap to true? /* jshint strict: false, newcap: false */var EventEmitter=require('./events').EventEmitter;function Compiler(){// Public this.compile=compile;this.setGlobals=setGlobals;this.addEventListener=addEvent;this.removeEventListener=removeEvent;// Private var MAX_PLACEABLE_LENGTH=2500;var _emitter=new EventEmitter();var _globals=null;var _references={globals:{}};var _entryTypes={Entity:Entity,Macro:Macro};// Public API functions function compile(ast,env){if(!env){env={};}for(var i=0,entry;entry=ast.body[i];i++){var Constructor=_entryTypes[entry.type];if(Constructor){try{env[entry.id.name]=new Constructor(entry,env);}catch(e){// rethrow non-compiler errors; requireCompilerError(e);// or, just ignore the error; it's been already emitted }}}return env;}function setGlobals(globals){_globals=globals;return true;}function addEvent(type,listener){return _emitter.addEventListener(type,listener);}function removeEvent(type,listener){return _emitter.removeEventListener(type,listener);}// utils function emit(Ctor,message,entry,source){var e=new Ctor(message,entry,source);_emitter.emit('error',e);return e;}// The Entity object. function Entity(node,env){this.id=node.id.name;this.env=env;this.local=node.local||false;this.index=null;this.attributes=null;this.publicAttributes=null;var i;if(node.index){this.index=[];for(i=0;i<node.index.length;i++){this.index.push(IndexExpression(node.index[i],this));}}if(node.attrs){this.attributes={};this.publicAttributes=[];for(i=0;i<node.attrs.length;i++){var attr=node.attrs[i];this.attributes[attr.key.name]=new Attribute(attr,this);if(!attr.local){this.publicAttributes.push(attr.key.name);}}}// Bug 817610 - Optimize a fast path for String entities in the Compiler if(node.value&&node.value.type==='String'){this.value=node.value.content;}else{this.value=LazyExpression(node.value,this,this.index);}}Entity.prototype.getString=function E_getString(ctxdata){try{var locals={__this__:this,__env__:this.env};return _resolve(this.value,locals,ctxdata);}catch(e){requireCompilerError(e);// `ValueErrors` are not emitted in `StringLiteral` where they are // created, because if the string in question is being evaluated in an // index, we'll emit an `IndexError` instead. To avoid duplication, // `ValueErrors` are only be emitted if they actually make it to // here. See `IndexExpression` for an example of why they wouldn't. if(e instanceof ValueError){_emitter.emit('error',e);}throw e;}};Entity.prototype.get=function E_get(ctxdata){// reset `_references` to an empty state _references.globals={};// evaluate the entity and its attributes; if any globals are used in // the process, `toString` will populate `_references.globals` // accordingly. var entity={value:this.getString(ctxdata),attributes:{}};if(this.publicAttributes){entity.attributes={};for(var i=0,attr;attr=this.publicAttributes[i];i++){entity.attributes[attr]=this.attributes[attr].getString(ctxdata);}}entity.globals=_references.globals;return entity;};function Attribute(node,entity){this.key=node.key.name;this.local=node.local||false;this.index=null;if(node.index){this.index=[];for(var i=0;i<node.index.length;i++){this.index.push(IndexExpression(node.index[i],this));}}if(node.value&&node.value.type==='String'){this.value=node.value.content;}else{this.value=LazyExpression(node.value,entity,this.index);}this.entity=entity;}Attribute.prototype.getString=function A_getString(ctxdata){try{var locals={__this__:this.entity,__env__:this.entity.env};return _resolve(this.value,locals,ctxdata);}catch(e){requireCompilerError(e);if(e instanceof ValueError){_emitter.emit('error',e);}throw e;}};function Macro(node,env){this.id=node.id.name;this.env=env;this.local=node.local||false;this.expression=LazyExpression(node.expression,this);this.args=node.args;}Macro.prototype._call=function M_call(args,ctxdata){var locals={__this__:this,__env__:this.env};// the number of arguments passed must equal the macro's arity if(this.args.length!==args.length){throw new RuntimeError(this.id+'() takes exactly '+this.args.length+' argument(s) ('+args.length+' given)');}for(var i=0;i<this.args.length;i++){locals[this.args[i].id.name]=args[i];}var final=this.expression(locals,ctxdata);locals=final[0];final=final[1];return[locals,_resolve(final,locals,ctxdata)];};var EXPRESSION_TYPES={// Primary expressions. 'Identifier':Identifier,'ThisExpression':ThisExpression,'VariableExpression':VariableExpression,'GlobalsExpression':GlobalsExpression,// Value expressions. 'Number':NumberLiteral,'String':StringLiteral,'Hash':HashLiteral,'HashItem':Expression,'ComplexString':ComplexString,// Logical expressions. 'UnaryExpression':UnaryExpression,'BinaryExpression':BinaryExpression,'LogicalExpression':LogicalExpression,'ConditionalExpression':ConditionalExpression,// Member expressions. 'CallExpression':CallExpression,'PropertyExpression':PropertyExpression,'AttributeExpression':AttributeExpression,'ParenthesisExpression':ParenthesisExpression};// The 'dispatcher' expression constructor. Other expression constructors // call this to create expression functions for their components. For // instance, `ConditionalExpression` calls `Expression` to create // expression functions for its `test`, `consequent` and `alternate` // symbols. function Expression(node,entry,index){// An entity can have no value. It will be resolved to `null`. if(!node){return null;}if(!EXPRESSION_TYPES[node.type]){throw emit('CompilationError','Unknown expression type'+node.type);}if(index){index=index.slice();}return EXPRESSION_TYPES[node.type](node,entry,index);}function LazyExpression(node,entry,index){// An entity can have no value. It will be resolved to `null`. if(!node){return null;}var expr;return function(locals,ctxdata,prop){if(!expr){expr=Expression(node,entry,index);}return expr(locals,ctxdata,prop);};}function _resolve(expr,locals,ctxdata){// Bail out early if it's a primitive value or `null`. This is exactly // what we want. if(typeof expr==='string'||typeof expr==='boolean'||typeof expr==='number'||!expr){return expr;}// Check if `expr` is an Entity or an Attribute if(expr.value!==undefined){return _resolve(expr.value,locals,ctxdata);}// Check if `expr` is an expression if(typeof expr==='function'){var current=expr(locals,ctxdata);locals=current[0];current=current[1];return _resolve(current,locals,ctxdata);}// Throw if `expr` is a macro if(expr.expression){throw new RuntimeError('Uncalled macro: '+expr.id);}// Throw if `expr` is a non-primitive from ctxdata or a global throw new RuntimeError('Cannot resolve ctxdata or global of type '+(typeof expr==='undefined'?'undefined':(0,_typeof3.default)(expr)));}function Identifier(node){var name=node.name;return function identifier(locals){if(!locals.__env__.hasOwnProperty(name)){throw new RuntimeError('Reference to an unknown entry: '+name);}// The only thing we care about here is the new `__this__` so we // discard any other local variables. Note that because this is an // assignment to a local variable, the original `locals` passed is not // changed. locals={__this__:locals.__env__[name],__env__:locals.__env__};return[locals,locals.__this__];};}function ThisExpression(){return function thisExpression(locals){return[locals,locals.__this__];};}function VariableExpression(node){var name=node.id.name;return function variableExpression(locals,ctxdata){if(locals.hasOwnProperty(name)){// locals[name] is already a [locals, value] tuple on its own return locals[name];}if(!ctxdata||!ctxdata.hasOwnProperty(name)){throw new RuntimeError('Reference to an unknown variable: '+name);}return[locals,ctxdata[name]];};}function GlobalsExpression(node){var name=node.id.name;return function globalsExpression(locals){if(!_globals){throw new RuntimeError('No globals set (tried @'+name+')');}if(!_globals.hasOwnProperty(name)){throw new RuntimeError('Reference to an unknown global: '+name);}var value;try{value=_globals[name].get();}catch(e){throw new RuntimeError('Cannot evaluate global '+name);}_references.globals[name]=true;return[locals,value];};}function NumberLiteral(node){return function numberLiteral(locals){return[locals,node.value];};}function StringLiteral(node){return function stringLiteral(locals,ctxdata,key){// if a key was passed, throw; checking arguments is more reliable // than testing the value of key because if the key comes from context // data it can be any type, also undefined if(key!==undefined){throw new RuntimeError('Cannot get property of a string: '+key);}return[locals,node.content];};}function ComplexString(node,entry){var content=[];for(var i=0;i<node.content.length;i++){content.push(Expression(node.content[i],entry));}// Every complexString needs to have its own `dirty` flag whose state // persists across multiple calls to the given complexString. On the // other hand, `dirty` must not be shared by all complexStrings. Hence // the need to define `dirty` as a variable available in the closure. // Note that the anonymous function is a self-invoked one and it returns // the closure immediately. return function(){var dirty=false;return function complexString(locals,ctxdata,key){if(key!==undefined){throw new RuntimeError('Cannot get property of a string: '+key);}if(dirty){throw new RuntimeError('Cyclic reference detected');}dirty=true;var parts=[];try{for(var i=0;i<content.length;i++){var part=_resolve(content[i],locals,ctxdata);if(typeof part!=='string'&&typeof part!=='number'){throw new RuntimeError('Placeables must be strings or '+'numbers');}if(part.length>MAX_PLACEABLE_LENGTH){throw new RuntimeError('Placeable has too many characters, '+'maximum allowed is '+MAX_PLACEABLE_LENGTH);}parts.push(part);}}catch(e){requireCompilerError(e);// only throw, don't emit yet. If the `ValueError` makes it to // `getString()` it will be emitted there. It might, however, be // cought by `IndexExpression` and changed into a `IndexError`. // See `IndexExpression` for more explanation. throw new ValueError(e.message,entry,node.source);}finally{dirty=false;}return[locals,parts.join('')];};}();}function IndexExpression(node,entry){var expression=Expression(node,entry);// This is analogous to `ComplexString` in that an individual index can // only be visited once during the resolution of an Entity. `dirty` is // set in a closure context of the returned function. return function(){var dirty=false;return function indexExpression(locals,ctxdata){if(dirty){throw new RuntimeError('Cyclic reference detected');}dirty=true;var retval;try{// We need to resolve `expression` here so that we catch errors // thrown deep within. Without `_resolve` we might end up with an // unresolved Entity object, and no "Cyclic reference detected" // error would be thown. retval=_resolve(expression,locals,ctxdata);}catch(e){// If it's an `IndexError` thrown deeper within `expression`, it // has already been emitted by its `indexExpression`. We can // safely re-throw it here. if(e instanceof IndexError){throw e;}// Otherwise, make sure it's a `RuntimeError` or a `ValueError` and // throw and emit an `IndexError`. // // If it's a `ValueError` we want to replace it by an `IndexError` // here so that `ValueErrors` from the index don't make their way // up to the context. The context only cares about ValueErrors // thrown by the value of the entity it has requested, not entities // used in the index. // // To illustrate this point with an example, consider the following // two strings, where `foo` is a missing entity. // // <prompt1["remove"] { // remove: "Remove {{ foo }}?", // keep: "Keep {{ foo }}?" // }> // // `prompt1` will throw a `ValueError`. The context can use it to // display the source of the entity, i.e. `Remove {{ foo }}?`. The // index resolved properly, so at least we know that we're showing // the right variant of the entity. // // <prompt2["{{ foo }}"] { // remove: "Remove file?", // keep: "Keep file?" // }> // // On the other hand, `prompt2` will throw an `IndexError`. This // is a more serious scenario for the context. We should not // assume that we know which variant to show to the user. In fact, // in the above (much contrived, but still) example, showing the // incorrect variant will likely lead to data loss. The context // should be more strict in this case and should not try to recover // from this error too hard. requireC