This repository has been archived by the owner on Nov 9, 2017. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 57
/
app.js.map
1 lines (1 loc) · 131 KB
/
app.js.map
1
{"version":3,"sources":["app.js","AppConfig.js","AppConstants.js","AppCtrl.js","editor/EditorContentCtrl.js","editor/EditorCtrl.js","editor/EditorDetailsCtrl.js","editor/EditorService.js","editor/EditorShortcuts.js","editor/EditorSuggestionsCtrl.js","components/blur-on/blurOnDirective.js","components/clickElsewhere/clickElsewhereDirective.js","components/document/DocumentService.js","components/dropdown/DropdownController.js","components/dropdown/DropdownService.js","components/dropdown/dropdown.js","components/dropdown/dropdownDirective.js","components/event/EventService.js","components/icon/iconDirective.js","components/locale/LocaleService.js","components/logo-loader/logoLoaderDirective.js","components/message/MessageHandler.js","components/notification/NotificationService.js","components/phrases/PhraseCache.js","components/phrases/PhraseService.js","components/progressbar/progressbarDirective.js","components/project/ProjectService.js","components/renderWhitespaceCharacters/renderWhitespaceCharacters.js","components/scrollbar-width/ScrollbarWidthCtrl.js","components/scrollbar-width/scrollbarWidthDirective.js","components/toggle/toggleDirective.js","components/transStatus/TransStatusService.js","components/transUnit/TransUnitCtrl.js","components/transUnit/TransUnitService.js","components/transUnit/transUnitDirective.js","components/transUnitFilter/transUnitFilterDirective.js","components/user/UserService.js","components/util/FilterUtil.js","components/util/PhraseUtil.js","components/util/StatisticUtil.js","components/util/StringUtil.js","components/util/UrlService.js"],"names":[],"mappings":"AAAA;EACE;;EAEA;GACC;GACA;GACA;EACD;IACE;MACE;MACA;MACA;MACA;MACA;MACA;MACA;MACA;IACF;;AAEJ;;ACnBA;EACE;;EAEA;GACC;GACA;GACA;GACA;EACD;IACE;;IAEA;IACA;MACE;QACE;UACE;UACA;UACA;QACF;QACA;UACE;UACA;UACA;UACA;QACF;QACA;UACE;UACA;UACA;QACF;QACA;UACE;UACA;UACA;YACE;UACF;YACE;cACE;UACJ;YACE;UACF;UACA;QACF;MACF;IACF,EAAE;;;IAEF;;IAEA;IACA;;IAEA;MACE;QACE;QACA;QACA,aAAa,oBAAoB;QACjC;UACE,MAAM,eAAA;YACJ;UACF,CAAC;QACH;MACF;QACE;QACA;UACE;YACE;YACA,aAAa,kCAAkC;UACjD;UACA;YACE;YACA,aAAa,0CAA0C;UACzD;UACA;YACE;YACA,aAAa,kCAAkC;UACjD;QACF;MACF;QACE;QACA;MACF;;MAEA;;EAEJ;EACA;EACA,CAAC;;;EAED;IACE;IACA;;AAEJ;;;;;AC5FA;EACE;;EAEA;GACC;GACA;GACA;EACD;IACE;IACA;IACA;IACA;IACA;IACA;;AAEJ;;;;ACfA;EACE;;EAEA;GACC;GACA;GACA;GACA;EACD;mBACiB;IACf;;IAEA;IACA;IACA;IACA;;IAEA;MACE;MACA;QACE;QACA;MACF;;MAEA;QACE;QACA;MACF;IACF;;IAEA;MACE;MACA;;IAEF;IACA;MACE;MACA;MACA;QACE;QACA;YACI;gBACI;kBACE;gBACF;gBACA;kBACE;oBACE;oBACA;kBACF;oBACE;kBACF;gBACF;MACV;QACE;UACE;MACJ;IACF;;IAEA;MACE;IACF;;IAEA;MACE;IACF;;IAEA;MACE;QACE;UACE;UACA;UACA;YACE;QACJ;UACE;QACF;IACJ;;IAEA;MACE;QACE;UACE;YACE;cACE;cACA;YACF;YACA;UACF;UACA;YACE;UACF;YACE;UACF;QACF;QACA;UACE;YACE;YACA;UACF;QACF;IACJ;EACF,CAAC;;;EAED;IACE;IACA,aAAa,OAAO;;AAExB;;;;;AC5GA;EACE;;EAEA;GACC;GACA;GACA;EACD;6BAC2B;6BACA;6BACA;;IAEzB;IACA;QACI;IACJ;;IAEA;;IAEA;MACE;MACA;;IAEF;;IAEA;MACE;QACE;UACE;QACF;UACE;UACA;YACE;cACE;YACF;UACF;UACA;QACF;QACA;QACA;MACF;;IAEF;MACE;;MAEA;QACE;QACA;UACE;UACA;QACF;MACF;MACA;QACE;MACF;IACF;;;IAGA;MACE;QACE;UACE;UACA;QACF;MACF;;IAEF;MACE;QACE;UACE;UACA;QACF;MACF;;IAEF;MACE;QACE;UACE;UACA;QACF;MACF;;IAEF;MACE;QACE;UACE;UACA;QACF;MACF;;IAEF;MACE;MACA;KACD;IACD;IACA;;IAEA;IACA;;IAEA;IACA;mBACe;;IAEf;MACE;QACE;QACA;QACA;QACA;;MAEF;QACE;MACF;MACA;MACA;;MAEA;QACE;+BACuB;iCACE;iCACA;iCACA;+BACF;MACzB;QACE;QACA;QACA;WACG;aACE;aACA;aACA;aACA;WACF;MACL;IACF;;IAEA;MACE;QACE;QACA;QACA;QACA;;MAEF;QACE;MACF;MACA;MACA;;MAEA;QACE;+BACuB;iCACE;iCACA;iCACA;+BACF;MACzB;QACE;QACA;QACA;WACG;aACE;aACA;aACA;aACA;WACF;MACL;IACF;;IAEA;MACE;QACE;QACA;QACA;;MAEF;QACE;MACF;;MAEA;QACE;UACE;QACF;UACE;iCACuB;mCACE;mCACA;mCACA;iCACF;UACvB;QACF;MACF;MACA;MACA;IACF;;IAEA;MACE;MACA;IACF;;IAEA;KACC;KACA;KACA;KACA;KACA;KACA;KACA;IACD;MACE;QACE;UACE;UACA;UACA;UACA;QACF;MACF;;MAEA;QACE;UACE;UACA;YACE;cACE;UACJ;;UAEA;YACE;;UAEF;MACJ;IACF;;IAEA;MACE;MACA;QACE;IACJ;;IAEA;MACE;IACF;;IAEA;EACF,CAAC;;;EAED;IACE;IACA,aAAa,iBAAiB;AAClC;;AC3PA;EACE;;EAEA;GACC;GACA;GACA;EACD;IACE;IACA;IACA;IACA;IACA;IACA;IACA;MACE;QACE;QACA;QACA;QACA;UACE;QACF;QACA;MACF;IACF;;IAEA;IACA;MACE;QACE;QACA;QACA;QACA;QACA;MACF;IACF;;IAEA;;IAEA;IACA;IACA;MACE;MACA;;MAEA;QACE;QACA;UACE;YACE;UACF;QACF;QACA;MACF;IACF;;IAEA;MACE;MACA;QACE;QACA;MACF;IACF;;IAEA;KACC;KACA;KACA;KACA;IACD;MACE;MACA;QACE;MACF;;MAEA;MACA;QACE;IACJ;;IAEA;IACA;;IAEA;IACA;IACA;MACE;MACA;MACA;;IAEF;MACE;IACF;;IAEA;MACE;QACE;IACJ;;IAEA;MACE;IACF;;IAEA;MACE;QACE;MACF;MACA;QACE;UACE;MACJ;;IAEF;MACE;MACA;QACE;QACA;UACE;UACA;YACE;YACA;QACJ;UACE;UACA;UACA;;UAEA;YACE;YACA;UACF;YACE;YACA;cACE;cACA;YACF;UACF;QACF;MACF;QACE;MACF;;IAEF;MACE;MACA;QACE;;QAEA;UACE;UACA;YACE;YACA;QACJ;UACE;UACA;cACI;UACJ;YACE;YACA;UACF;YACE;YACA;cACE;cACA;YACF;UACF;QACF;MACF;QACE;MACF;;IAEF;MACE;QACE;QACA;MACF;;IAEF;MACE;QACE;QACA;MACF;;IAEF;MACE;;QAEE;UACE;;QAEF;QACA;MACF;;IAEF;MACE;QACE;MACF;QACE;UACE;MACJ;IACF;;IAEA;MACE;IACF;;IAEA;MACE;IACF;;IAEA;MACE;IACF;;;IAGA;MACE;IACF;;IAEA;MACE;IACF;;IAEA;MACE;IACF;;IAEA;MACE;IACF;;IAEA;MACE;QACE;MACF;QACE;QACA;UACE;YACE;QACJ;MACF;IACF;;IAEA;MACE;MACA;MACA;MACA;MACA;;MAEA;QACE;UACE;MACJ;IACF;;IAEA;MACE;QACE;QACA;IACJ;;IAEA;MACE;QACE;UACE;UACA;QACF;MACF;IACF;;IAEA;MACE;IACF;;IAEA;KACC;KACA;KACA;KACA;KACA;KACA;KACA;IACD;MACE;QACE;YACI;cACE;YACF;cACE;UACJ;UACA;YACE;UACF;IACN;;IAEA;;IAEA;EACF,CAAC;;;EAED;IACE;IACA,aAAa,UAAU;AAC3B;;AC/SA;EACE;;EAEA;GACC;GACA;GACA;EACD;IACE;;IAEA;EACF;;EAEA;IACE;IACA,aAAa,iBAAiB;AAClC;;AChBA;EACE;;EAEA;GACC;GACA;GACA;GACA;EACD;IACE;IACA;IACA;QACI;;IAEJ;;IAEA;IACA;;IAEA;MACE;QACE;UACE;UACA;UACA;UACA;UACA;UACA;QACF;QACA;MACF;;IAEF;2CACuC;MACrC;QACE;MACF;MACA;QACE;MACF;MACA;QACE;MACF;MACA;QACE;MACF;IACF;;IAEA;KACC;KACA;KACA;KACA;KACA;KACA;IACD;MACE;QACE;YACI;QACJ;UACE;UACA;QACF;;QAEA;QACA;UACE;UACA;UACA;QACF;UACE;UACA;YACE;YACA;YACA;YACA;UACF;QACF;QACA;QACA;MACF;;IAEF;MACE;QACE;IACJ;;IAEA;IACA;MACE;;MAEA;;MAEA;QACE;UACE;UACA;YACE;UACF;QACF;MACF;MACA;QACE;QACA;QACA;QACA;QACA;QACA;QACA;MACF;;MAEA;QACE;UACE;;UAEA;YACE;;UAEF;YACE;YACA;YACA;;UAEF;YACE;QACJ;QACA;UACE;YACE;UACF;UACA;YACE;QACJ;MACF;IACF;;IAEA;MACE;QACE;MACF;MACA;IACF;;IAEA;EACF,CAAC;;;EAED;IACE;IACA,UAAU,aAAa;;AAE3B;;;ACtJA;EACE;;EAEA;GACC;GACA;GACA;GACA;EACD;2BACyB;IACvB;MACE;;IAEF;IACA;IACA;;IAEA;MACE;QACE;QACA;UACE;MACJ;IACF;;IAEA;MACE;QACE;QACA;QACA;UACE;MACJ;IACF;;IAEA;MACE;QACE;QACA;QACA;UACE;MACJ;IACF;;IAEA;MACE;MACA;MACA;QACE;QACA;UACE;QACF;MACF;QACE;QACA;UACE;UACA;YACE;QACJ;UACE;UACA;YACE;QACJ;MACF;IACF;;IAEA;MACE;QACE;QACA;QACA;UACE;YACE;YACA;YACA;YACA;UACF;MACJ;IACF;;IAEA;KACC;KACA;KACA;KACA;IACD;MACE;MACA;MACA;MACA;QACE;UACE;YACE;YACA;UACF;;QAEF;QACA;QACA;MACF;IACF;;IAEA;KACC;KACA;KACA;IACD;MACE;QACE;;MAEF;;MAEA;QACE;;MAEF;QACE;;MAEF;MACA;MACA;QACE;MACF;;MAEA;QACE;MACF;;MAEA;QACE;MACF;;MAEA;QACE;QACA;QACA;;MAEF;QACE;QACA;QACA;QACA;SACC;SACA;QACD;QACA;IACJ;;IAEA;KACC;;KAEA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;;IAED;KACC;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACD;IACA;MACE;MACA;QACE;MACF;MACA;QACE;QACA;UACE;MACJ;MACA;IACF;;IAEA;MACE;QACE;QACA;MACF;MACA;MACA;QACE;MACF;MACA;QACE;MACF;MACA;IACF;;IAEA;MACE;MACA;MACA;QACE;UACE;YACE;UACF;QACF;MACF;IACF;;IAEA;MACE;QACE;UACE;QACF;MACF;IACF;;IAEA;MACE;QACE;UACE;YACE;UACF;MACJ;IACF;;IAEA;MACE;QACE;MACF;IACF;;IAEA;MACE;MACA;QACE;QACA;QACA;QACA;QACA;UACE;QACF;MACF;IACF;;IAEA;uDACmD;MACjD;;MAEA;MACA;;MAEA;QACE;UACE;UACA;UACA;UACA;QACF;MACF;IACF;;IAEA;MACE;QACE;QACA;QACA;UACE;YACE;YACA;UACF;MACJ;IACF;;IAEA;KACC;KACA;KACA;KACA;KACA;KACA;IACD;MACE;MACA;MACA;QACE;QACA;MACF;IACF;;IAEA;KACC;KACA;KACA;KACA;KACA;IACD;MACE;QACE;QACA;QACA;QACA;QACA;QACA;QACA;QACA;MACF;MACA;;MAEA;QACE;QACA;UACE;YACE;YACA;UACF;YACE;UACF;QACF;;QAEA;MACF;;MAEA;IACF;;IAEA;EACF,CAAC;;;EAED;IACE;IACA,UAAU,eAAe;AAC7B;;;ACnVA;EACE;;EAEA;GACC;GACA;GACA;EACD;IACE;;IAEA;EACF;;EAEA;IACE;IACA,aAAa,qBAAqB;AACtC;;AChBA;EACE;;EAEA;GACC;GACA;GACA;GACA;EACD;IACE;MACE;MACA;QACE;UACE;cACI;YACF;UACF;QACF;IACJ;EACF;;EAEA;IACE;IACA,YAAY,MAAM;;AAEtB;;ACzBA;EACE;;EAEA;GACC;GACA;GACA;GACA;EACD;IACE;MACE;MACA;QACE;MACF;MACA;QACE;UACE;YACE;UACF;QACF;;QAEA;;QAEA;UACE;QACF;MACF;IACF;EACF,CAAC;;;EAED;IACE;IACA,YAAY,cAAc;;AAE9B;;AClCA;EACE;;EAEA;GACC;GACA;GACA;GACA;GACA;GACA;EACD;2BACyB;IACvB;QACI;;IAEJ;KACC;KACA;KACA;KACA;KACA;KACA;IACD;MACE;QACE;UACE;UACA;YACE;YACA;UACF;UACA;QACF;MACF;MACA;IACF;;IAEA;KACC;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACD;MACE;MACA;QACE;QACA;UACE;QACF;UACE;UACA;YACE;cACE;cACA;gBACE;gBACA;gBACA;gBACA;cACF;cACA;YACF;UACF;UACA;;YAEE;YACA;cACE;gBACE;YACJ;;YAEA;YACA;UACF;QACF;MACF;IACF;;IAEA;KACC;KACA;KACA;KACA;IACD;MACE;IACF;;IAEA;KACC;KACA;KACA;KACA;IACD;MACE;IACF;;IAEA;MACE;SACG;MACH;IACF;;IAEA;+CAC2C;+CACA;MACzC;MACA;QACE;UACE;;QAEF;UACE;YACE;YACA;YACA;YACA;UACF;QACF;MACF;IACF;;IAEA;IACA;MACE;IACF;;IAEA;KACC;KACA;KACA;KACA;IACD;;MAEE;QACE;;MAEF;QACE;QACA;QACA;QACA;MACF;;MAEA;QACE;QACA;QACA;MACF;IACF;;IAEA;EACF,CAAC;;;EAED;IACE;IACA,UAAU,eAAe;AAC7B;;AC9JA;EACE;;EAEA;GACC;GACA;GACA;GACA;GACA;GACA;GACA;EACD;IACE;IACA;QACI;QACA;QACA;QACA;QACA;QACA;UACE;;IAEN;MACE;;MAEA;QACE;QACA;;QAEA;UACE;QACF;MACF;IACF;;IAEA;MACE;MACA;IACF;;IAEA;IACA;MACE;IACF;;IAEA;MACE;IACF;;IAEA;MACE;QACE;MACF;IACF;;IAEA;MACE;QACE;;MAEF;QACE;QACA;QACA;QACA;UACE;QACF;QACA;MACF;QACE;MACF;;MAEA;MACA;QACE;UACE;QACF;MACF;IACF;;IAEA;MACE;IACF;;IAEA;MACE;IACF;;IAEA;MACE;IACF;;IAEA;MACE;IACF;EACF,CAAC;;;EAED;IACE;IACA,aAAa,YAAY;;AAE7B;;ACpGA;EACE;;EAEA;GACC;GACA;GACA;GACA;GACA;GACA;GACA;;EAED;IACE;QACI;;IAEJ;MACE;QACE;QACA;MACF;;MAEA;QACE;MACF;;MAEA;IACF;;IAEA;MACE;QACE;QACA;QACA;MACF;IACF;;IAEA;MACE;QACE;MACF;MACA;MACA;QACE;MACF;;MAEA;QACE;MACF;IACF;;IAEA;MACE;QACE;QACA;MACF;IACF;EACF,CAAC;;;EAED;IACE;IACA,UAAU,eAAe;;AAE7B;;;AC/DA;EACE;;EAEA;GACC;GACA;GACA;GACA;GACA;GACA;EACD;IACE;EACF;;EAEA;IACE;IACA;;AAEJ;;AClBA;EACE;;EAEA;GACC;GACA;GACA;GACA;GACA;GACA;;EAED;IACE;MACE;MACA,aAAa,YAAY;MACzB;QACE;MACF;IACF;EACF;;EAEA;IACE;MACE;MACA;MACA;QACE;MACF;MACA;QACE;MACF;IACF;EACF;;EAEA;GACC;GACA;GACA;GACA;GACA;GACA;;EAED;IACE;MACE;MACA;MACA;QACE;UACE;QACF;;QAEA;;QAEA;UACE;UACA;;UAEA;YACE;cACE;YACF;UACF;QACF;;QAEA;;QAEA;QACA;UACE;UACA;QACF;QACA;UACE;UACA;YACE;UACF;QACF;;QAEA;UACE;QACF;MACF;IACF;EACF;;EAEA;IACE;IACA,YAAY,QAAQ;IACpB,YAAY,eAAe;IAC3B,YAAY,cAAc;;AAE9B;;;AC3FA;EACE;;EAEA;GACC;GACA;GACA;GACA;GACA;GACA;GACA;EACD;IACE;IACA;MACE;OACC;OACA;OACA;OACA;MACD;MACA;;MAEA;OACC;OACA;OACA;OACA;OACA;OACA;MACD;;MAEA;MACA;;MAEA;MACA;;MAEA;MACA;;MAEA;MACA;;MAEA;OACC;OACA;OACA;OACA;OACA;OACA;OACA;OACA;MACD;;MAEA;OACC;OACA;OACA;MACD;;MAEA;OACC;OACA;OACA;MACD;;MAEA;OACC;OACA;OACA;MACD;;MAEA;OACC;OACA;OACA;OACA;OACA;MACD;;MAEA;;MAEA;;MAEA;;MAEA;;MAEA;OACC;OACA;MACD;MACA;MACA;;MAEA;OACC;OACA;OACA;MACD;;;MAGA;OACC;OACA;MACD;IACF;;IAEA;KACC;KACA;KACA;KACA;KACA;KACA;IACD;MACE;MACA;IACF;;IAEA;KACC;KACA;KACA;KACA;KACA;KACA;IACD;MACE;MACA;IACF;;IAEA;EACF,CAAC;;;EAED;IACE;IACA,UAAU,YAAY;AAC1B;;AC1IA;EACE;;EAEA;GACC;GACA;GACA;GACA;EACD;IACE;MACE;MACA;MACA;QACE;QACA;QACA;MACF;MACA;MACA;QACE;YACI;;QAEJ;;QAEA;UACE;QACF;;QAEA;QACA;UACE;YACE;YACA;UACF;QACF;MACF;IACF;EACF,CAAC;;;EAED;IACE;IACA,YAAY,IAAI;;AAEpB;;AC3CA;EACE;;EAEA;GACC;GACA;GACA;GACA;GACA;EACD;;IAEE;;IAEA;KACC;KACA;KACA;KACA;KACA;IACD;;MAEE;QACE;UACE;UACA;YACE;YACA;UACF;UACA;QACF;MACF;;MAEA;IACF;;IAEA;IACA;MACE;QACE;UACE;UACA;QACF;MACF;MACA;QACE;MACF;IACF;;IAEA;MACE;QACE;UACE;QACF;MACF;;MAEA;IACF;;IAEA;MACE;QACE;UACE;QACF;MACF;IACF;;IAEA;MACE;QACE;MACF;IACF;;IAEA;MACE;MACA;QACE;MACF;MACA;IACF;;IAEA;MACE;MACA;MACA;MACA;MACA;MACA;MACA;QACE;QACA;MACF;IACF;EACF,CAAC;;;EAED;IACE;IACA,UAAU,aAAa;AAC3B;;ACjGA;;EAEE;;EAEA;GACC;GACA;GACA;GACA;GACA;GACA;GACA;EACD;IACE;MACE;MACA;QACE;QACA;MACF;MACA;QACE;;QAEA;UACE;QACF;;QAEA;UACE;QACF;;QAEA;UACE;YACE;UACF;YACE;UACF;QACF;MACF;MACA;IACF;EACF,CAAC;;;EAED;IACE;IACA,YAAY,UAAU;;AAE1B;;AC9CA;EACE;;EAEA;GACC;GACA;GACA;EACD;IACE;MACE;QACE;MACF;MACA;QACE;MACF;MACA;QACE;MACF;IACF;EACF;;EAEA;IACE;IACA,UAAU,cAAc;;AAE5B;;ACzBA;EACE;;EAEA;GACC;GACA;GACA;GACA;GACA;EACD;;EAEA;;EAEA;IACE;IACA,UAAU,mBAAmB;;AAEjC;;ACjBA;EACE;;EAEA;GACC;GACA;GACA;GACA;GACA;EACD;uBACqB;IACnB;MACE;MACA;;IAEF;MACE;QACE;QACA;UACE;QACF;UACE;UACA;cACI;gBACE;gBACA;kBACE;kBACA;kBACA;kBACA;gBACF;gBACA;cACF;YACF;YACA;UACF;YACE;YACA;YACA;UACF;QACF;MACF;;IAEF;MACE;QACE;QACA;MACF;QACE;UACE;YACE;UACF;YACE;UACF;QACF;UACE;QACF;MACF;MACA;QACE;MACF;MACA;QACE;QACA;UACE;YACE;cACE;cACA;gBACE;gBACA;cACF;YACF;UACF;QACF;QACA;UACE;YACE;cACE;cACA;gBACE;gBACA;cACF;YACF;UACF;QACF;;QAEA;QACA;UACE;YACE;QACJ;UACE;QACF;UACE;QACF;MACF;;MAEA;QACE;QACA;UACE;UACA;UACA;QACF;QACA;MACF;;MAEA;QACE;QACA;UACE;UACA;QACF;QACA;MACF;IACF;;IAEA;KACC;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACD;MACE;;QAEE;UACE;;QAEF;UACE;QACF;QACA;QACA;UACE;QACF;;QAEA;QACA;QACA;UACE;QACF;QACA;QACA;QACA;MACF;;IAEF;MACE;QACE;IACJ;;IAEA;EACF,CAAC;;;EAED;IACE;IACA,UAAU,WAAW;;AAEzB;;AClKA;EACE;;EAEA;GACC;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACD;GACC;GACA;GACA;GACA;GACA;EACD;yBACuB;IACrB;;IAEA;;IAEA;IACA;IACA;IACA;;IAEA;MACE;QACE;UACE;UACA;QACF;IACJ;;IAEA;KACC;KACA;IACD;6CACyC;;MAEvC;;MAEA;QACE;;MAEF;QACE;QACA;UACE;YACE;UACF;YACE;UACF;QACF;QACA;QACA;QACA;UACE;MACJ;;MAEA;OACC;OACA;OACA;OACA;OACA;MACD;QACE;UACE;cACI;UACJ;YACE;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;cACE;YACF;YACA;UACF;QACF;MACF;;MAEA;QACE;UACE;QACF;QACA;MACF;;MAEA;QACE;UACE;YACE;cACE;gBACE;cACF;cACA;YACF;YACA;UACF;MACJ;IACF;;IAEA;IACA;MACE;;MAEA;QACE;;MAEF;MACA;MACA;QACE;QACA;QACA;MACF;IACF;;IAEA;IACA;MACE;MACA;QACE;MACF;IACF;;IAEA;IACA;MACE;mCAC6B;mCACA;QAC3B;UACE;YACE;UACF;YACE;UACF;UACA;YACE;UACF;QACF;IACJ;;IAEA;IACA;MACE;mCAC6B;mCACA;QAC3B;UACE;YACE;UACF;YACE;UACF;UACA;UACA;QACF;IACJ;;IAEA;IACA;MACE;mCAC6B;mCACA;QAC3B;UACE;YACE;YACA;;UAEF;YACE;UACF;;UAEA;YACE;cACE;YACF;cACE;YACF;UACF;UACA;QACF;IACJ;;IAEA;MACE;QACE;MACF;IACF;;IAEA;MACE;QACE;MACF;MACA;QACE;MACF;IACF;;IAEA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;IAEA;EACF,CAAC;;;EAED;IACE;IACA,UAAU,aAAa;;AAE3B;;;AClOA;EACE;;EAEA;GACC;GACA;GACA;GACA;EACD;IACE;MACE;MACA;MACA;QACE;QACA;MACF;MACA;MACA,YAAY,WAAA;QACV;SACC;SACA;SACA;QACD;UACE;YACE;UACF;QACF;MACF,CAAC;IACH;EACF;;EAEA;IACE;QACI;QACA;QACA;QACA;QACA;QACA;QACA;UACE;QACF;;IAEJ;MACE;MACA;IACF;IACA;MACE;MACA;IACF;IACA;MACE;MACA;IACF;IACA;MACE;MACA;IACF;IACA;EACF;;EAEA;IACE;IACA;MACE;IACF;IACA;EACF;;EAEA;IACE;IACA,YAAY,WAAW;;AAE3B;;AC1EA;EACE;;EAEA;GACC;GACA;GACA;GACA;;EAED;;IAEE;KACC;KACA;KACA;KACA;KACA;IACD;MACE;UACI;YACE;YACA;cACE;YACF;UACF;QACF;;MAEF;MACA;IACF;;IAEA;MACE;IACF;EACF,CAAC;;EACD;IACE;IACA,UAAU,cAAc;AAC5B;;ACtCA;EACE;;EAEA;GACC;GACA;GACA;GACA;GACA;EACD;IACE;MACE;QACE;QACA;MACF;MACA;QACE;QACA;MACF;MACA;QACE;QACA;MACF;IACF;;IAEA;MACE;MACA;MACA;QACE;MACF;;MAEA;QACE;UACE;UACA;UACA;UACA;QACF;MACF;IACF;;IAEA;MACE;IACF;EACF;;EAEA;IACE;IACA,YAAY,0BAA0B;;AAE1C;;ACnDA;EACE;;EAEA;GACC;GACA;GACA;GACA;GACA;GACA;GACA;EACD;IACE;;IAEA;MACE;UACI;UACA;;MAEJ;IACF;;EAEF;;EAEA;IACE;IACA,aAAa,kBAAkB;;AAEnC;;AC5BA;EACE;;EAEA;GACC;GACA;GACA;GACA;GACA;GACA;EACD;IACE;MACE;MACA,aAAa,wCAAwC;MACrD;QACE;MACF;IACF;EACF;;EAEA;GACC;GACA;GACA;GACA;GACA;EACD;IACE;MACE;MACA;MACA;QACE;UACE;QACF;QACA;QACA;MACF;IACF;EACF;;EAEA;GACC;GACA;GACA;GACA;GACA;EACD;IACE;MACE;MACA;MACA;QACE;UACE;QACF;QACA;MACF;IACF;EACF;;EAEA;GACC;GACA;GACA;GACA;GACA;EACD;IACE;MACE;MACA;MACA;QACE;UACE;QACF;QACA;MACF;IACF;EACF;;EAEA;IACE;IACA,YAAY,cAAc;IAC1B,YAAY,qBAAqB;IACjC,YAAY,uBAAuB;IACnC,YAAY,mBAAmB;;AAEnC;;ACrFA;EACE;;EAEA;GACC;GACA;GACA;GACA;GACA;EACD;IACE;MACE;MACA;QACE;MACF;IACF;EACF;;EAEA;IACE;IACA,YAAY,cAAc;;AAE9B;;ACtBA;EACE;;EAEA;GACC;GACA;GACA;GACA;GACA;GACA;EACD;GACC;GACA;GACA;GACA;EACD;IACE;QACI;UACE;YACE;YACA;YACA;UACF;UACA;YACE;YACA;YACA;UACF;UACA;YACE;YACA;YACA;UACF;UACA;YACE;YACA;YACA;UACF;QACF;;IAEJ;MACE;IACF;;IAEA;MACE;IACF;;IAEA;KACC;KACA;KACA;KACA;IACD;MACE;IACF;;IAEA;MACE;IACF;;IAEA;MACE;IACF;;IAEA;MACE;IACF;;IAEA;MACE;IACF;;IAEA;KACC;KACA;KACA;KACA;KACA;KACA;IACD;MACE;MACA;QACE;MACF;QACE;MACF;MACA;IACF;;IAEA;KACC;KACA;KACA;KACA;KACA;KACA;IACD;MACE;MACA;QACE;MACF;QACE;MACF;MACA;IACF;;IAEA;EACF,CAAC;;;EAED;IACE;IACA,UAAU,kBAAkB;AAChC;;;ACjHA;EACE;;EAEA;GACC;GACA;GACA;EACD;yBACuB;yBACA;;IAErB;;IAEA;IACA;IACA;;IAEA;MACE;;IAEF;MACE;QACE;QACA;MACF;IACF;;IAEA;IACA;MACE;MACA;QACE;MACF;MACA;QACE;UACE;YACE;YACA;UACF;MACJ;IACF;;IAEA;MACE;UACI;IACN;;IAEA;MACE;IACF;;IAEA;MACE;MACA;QACE;UACE;YACE;YACA;MACN;IACF;;IAEA;MACE;MACA;QACE;IACJ;;IAEA;MACE;MACA;QACE;IACJ;;IAEA;MACE;MACA;QACE;IACJ;;IAEA;MACE;IACF;;IAEA;MACE;IACF;;IAEA;MACE;QACE;MACF;QACE;QACA;MACF;IACF;;IAEA;MACE;IACF;;IAEA;MACE;MACA;IACF;;IAEA;MACE;QACE;MACF;QACE;UACE;MACJ;MACA;QACE;MACF;MACA;IACF;;IAEA;MACE;MACA;QACE;MACF;QACE;UACE;MACJ;MACA;IACF;;IAEA;MACE;IACF;;IAEA;MACE;QACE;+BACuB;iCACE;iCACA;+BACF;MACzB;IACF;;IAEA;MACE;QACE;UACE;YACE;cACE;cACA;QACN;MACF;IACF;;IAEA;EACF,CAAC;;;EAED;IACE;IACA,aAAa,aAAa;AAC9B;;;AChKA;EACE;;EAEA;GACC;GACA;GACA;GACA;GACA;GACA;EACD;IACE;IACA;IACA;QACI;QACA;;IAEJ;MACE;IACF;;IAEA;MACE;IACF;;IAEA;MACE;QACE;QACA;UACE;QACF;IACJ;;IAEA;KACC;KACA;KACA;KACA;KACA;IACD;MACE;QACE;YACI;YACA;;QAEJ;UACE;;UAEA;YACE;;YAEA;YACA;cACE;cACA;gBACE;kBACE;kBACA;kBACA;kBACA;gBACF;YACJ;UACF;;UAEA;UACA;UACA;;UAEA;;UAEA;UACA;YACE;cACE;gBACE;gBACA;cACF;YACF;cACE;cACA;YACF;UACF;QACF;UACE;QACF;MACF;;IAEF;KACC;KACA;KACA;IACD;MACE;QACE;QACA;UACE;UACA;UACA;YACE;YACA;YACA;YACA;cACE;cACA;YACF;UACF;QACF;QACA;MACF;;IAEF;KACC;KACA;KACA;IACD;MACE;QACE;UACE;QACF;MACF;;IAEF;KACC;KACA;KACA;IACD;MACE;QACE;UACE;UACA;UACA;QACF;;QAEA;QACA;UACE;QACF;;QAEA;QACA;UACE;YACE;UACF;QACF;MACF;;IAEF;KACC;KACA;KACA;IACD;OACG;;IAEH;KACC;KACA;KACA;IACD;OACG;;IAEH;MACE;MACA;MACA;IACF;OACG;;IAEH;MACE;MACA;MACA;IACF;OACG;;IAEH;MACE;MACA;QACE;QACA;MACF;MACA;MACA;QACE;MACF;QACE;IACJ;;IAEA;MACE;MACA;;MAEA;QACE;MACF;QACE;IACJ;;IAEA;OACG;OACA;IACH;;IAEA;MACE;MACA;MACA;QACE;IACJ;;IAEA;MACE;MACA;QACE;MACF;IACF;;IAEA;MACE;MACA;IACF;;IAEA;KACC;KACA;KACA;KACA;KACA;KACA;KACA;IACD;MACE;MACA;QACE;MACF;MACA;MACA;;MAEA;QACE;UACE;UACA;QACF;YACI;YACA;QACJ;MACF;;MAEA;QACE;QACA;MACF;;MAEA;IACF;;IAEA;EACF,CAAC;;;EAED;IACE;IACA,UAAU,gBAAgB;AAC9B;;;;ACrQA;EACE;;EAEA;GACC;GACA;GACA;GACA;EACD;IACE;MACE;MACA;MACA;QACE;QACA;QACA;MACF;MACA,aAAa,8BAA8B;MAC3C;MACA;QACE;MACF;IACF;EACF;;EAEA;IACE;IACA,YAAY,SAAS;;AAEzB;;AC7BA;EACE;;EAEA;GACC;GACA;GACA;GACA;EACD;IACE;MACE;MACA;MACA;QACE;MACF;MACA;IACF;EACF;;EAEA;IACE;IACA,YAAY,eAAe;;AAE/B;;ACvBA;EACE;;EAEA;GACC;GACA;GACA;GACA;EACD;;IAEE;MACE;QACE;UACE;UACA;YACE;UACF;QACF;MACF;MACA;IACF;;IAEA;MACE;QACE;UACE;QACF;MACF;MACA;IACF;;IAEA;MACE;QACE;UACE;QACF;MACF;MACA;MACA;IACF;EACF,CAAC;;EACD;IACE;IACA,UAAU,WAAW;AACzB;;AC5CA;EACE;;EAEA;GACC;GACA;GACA;GACA;EACD;;IAEE;KACC;KACA;KACA;KACA;KACA;KACA;KACA;IACD;MACE;QACE;MACF;MACA;QACE;MACF;IACF;;IAEA;KACC;KACA;KACA;IACD;MACE;MACA;QACE;MACF;MACA;QACE;MACF;MACA;IACF;;IAEA;MACE;MACA;QACE;MACF;MACA;QACE;MACF;MACA;IACF;;;IAGA;MACE;QACE;MACF;MACA;QACE;UACE;QACF;MACF;IACF;;IAEA;MACE;MACA;MACA;IACF;EACF,CAAC;;EACD;IACE;IACA,UAAU,UAAU;AACxB;;AC1EA;EACE;;EAEA;GACC;GACA;GACA;GACA;EACD;;IAEE;MACE;QACE;MACF;MACA;QACE;MACF;MACA;QACE;MACF;MACA;QACE;MACF;IACF;;IAEA;MACE;MACA;MACA;QACE;UACE;YACE;QACJ;MACF;IACF;;IAEA;MACE;IACF;;IAEA;MACE;QACE;IACJ;;IAEA;MACE;IACF;;IAEA;MACE;MACA;MACA;MACA;IACF;EACF,CAAC;;EACD;IACE;IACA,UAAU,UAAU;AACxB;;AC3DA;EACE;;EAEA;GACC;GACA;GACA;GACA;GACA;GACA;;EAED;IACE;MACE;QACE;MACF;MACA;QACE;MACF;IACF;EACF;EACA;IACE;IACA,UAAU,aAAa;AAC3B;;ACxBA;EACE;;EAEA;GACC;GACA;GACA;GACA;;EAED;IACE;MACE;QACE;QACA;MACF;MACA;IACF;;IAEA;MACE;QACE;QACA;MACF;MACA;IACF;;IAEA;MACE;QACE;QACA;MACF;MACA;IACF;;IAEA;MACE;MACA;MACA;IACF;EACF;EACA;IACE;IACA,UAAU,UAAU;AACxB;;AC3CA;EACE;;EAEA;GACC;GACA;GACA;GACA;GACA;EACD;IACE;IACA;MACE;QACE;QACA;IACJ;;IAEA;MACE;MACA;MACA;MACA;MACA;QACE;;IAEJ;;IAEA;MACE;QACE;MACF;MACA;QACE;SACC;SACA;SACA;SACA;SACA;SACA;SACA;SACA;SACA;QACD;UACE;UACA;YACE;UACF;YACE;gBACI;;YAEJ;YACA;cACE;YACF;YACA;cACE;YACF;UACF;;UAEA;UACA;UACA;UACA;UACA;YACE;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;UACF;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;YACE;cACE;UACJ;;UAEA;YACE;QACJ;MACF;IACF;;IAEA;KACC;KACA;IACD;MACE;IACF;;IAEA;MACE;QACE;IACJ;;IAEA;MACE;IACF;;IAEA;;IAEA;;IAEA;KACC;KACA;KACA;KACA;KACA;KACA;IACD;MACE;IACF;;IAEA;KACC;KACA;IACD;MACE;QACE;MACF;IACF;EACF,CAAC;;;EAED;IACE;IACA,UAAU,UAAU;AACxB","file":"app.js","sourcesContent":["(function() {\n 'use strict';\n\n /**\n * Root application\n * app.js\n */\n angular.module(\n 'app', [\n 'ngResource',\n 'ngAnimate',\n 'ui.router',\n 'templates',\n 'cfp.hotkeys',\n 'focusOn',\n 'monospaced.elastic',\n 'gettext'\n ]);\n\n})();\n","(function() {\n 'use strict';\n\n /**\n * @name AppConfig\n * @description Main config for the entire app\n * @ngInject\n */\n function AppConfig($stateProvider, $urlRouterProvider, $httpProvider,\n hotkeysProvider) {\n\n //Can't use injection for EventService as this module is out of the scope\n var interceptor = function($q, $rootScope) {\n return {\n request: function(config) {\n // See EventService.EVENT.LOADING_START\n $rootScope.$broadcast('loadingStart');\n return config;\n },\n requestError: function(rejection) {\n // See EventService.EVENT.LOADING_STOP\n $rootScope.$broadcast('loadingStop');\n console.error('Request error due to ', rejection);\n return $q.reject(rejection);\n },\n response: function(response) {\n // See EventService.EVENT.LOADING_STOP\n $rootScope.$broadcast('loadingStop');\n return response || $q.when(response);\n },\n responseError: function(rejection) {\n // See EventService.EVENT.LOADING_STOP\n $rootScope.$broadcast('loadingStop');\n if (rejection.status === 401) {\n console.error('Unauthorized access. Please login');\n } else if (rejection.status === 404) {\n console.error('Service end point not found- ',\n rejection.config.url);\n } else {\n console.error('Error in response ', rejection);\n }\n return $q.reject(rejection);\n }\n };\n };\n\n $httpProvider.interceptors.push(interceptor);\n\n // For any unmatched url, redirect to /editor\n $urlRouterProvider.otherwise('/');\n\n $stateProvider\n .state('editor', {\n url: '/:projectSlug/:versionSlug/translate',\n templateUrl: 'editor/editor.html',\n controller: 'EditorCtrl as editor',\n resolve: {\n url : function(UrlService) {\n return UrlService.init();\n }\n }\n }).state('editor.selectedContext', {\n url: '/:docId/:localeId',\n views: {\n 'editor-content': {\n templateUrl: 'editor/editor-content.html',\n controller: 'EditorContentCtrl as editorContent'\n },\n 'editor-suggestions': {\n templateUrl: 'editor/editor-suggestions.html',\n controller: 'EditorSuggestionsCtrl as editorSuggestions'\n },\n 'editor-details': {\n templateUrl: 'editor/editor-details.html',\n controller: 'EditorDetailsCtrl as editorDetails'\n }\n }\n }).state('editor.selectedContext.tu', {\n url: '/?id&selected?states',\n reloadOnSearch: false\n });\n\n hotkeysProvider.includeCheatSheet = false;\n\n // $locationProvider.html5Mode(true);\n // .hashPrefix('!');\n }\n\n angular\n .module('app')\n .config(AppConfig);\n\n})();\n\n\n\n","(function() {\n 'use strict';\n\n /**\n * AddConstants\n * \"Global\" app variables. Don't worry David, they're not really global.\n */\n angular\n .module('app')\n .constant('_', window._)\n .constant('str', window._.string)\n .constant('Mousetrap', window.Mousetrap)\n // Toggle to hide/show features that are ready for production\n .constant('PRODUCTION', true);\n\n})();\n\n\n","(function() {\n 'use strict';\n\n /**\n * @name AppCtrl\n * @description Main controler for the entire app\n * @ngInject\n */\n function AppCtrl($scope, UserService, UrlService, LocaleService,\n MessageHandler, gettextCatalog, StringUtil, PRODUCTION) {\n var appCtrl = this;\n\n // See AppConstants.js\n appCtrl.PRODUCTION = PRODUCTION;\n appCtrl.settings = UserService.settings;\n appCtrl.uiLocaleList = [ LocaleService.DEFAULT_LOCALE ];\n\n /*\n Not used for the time being. But should show loading when change state\n $scope.$on('$stateChangeStart', function(event, toState) {\n if (toState.resolve) {\n }\n });\n\n $scope.$on('$stateChangeSuccess', function(event, toState) {\n if (toState.resolve) {\n }\n });\n */\n\n UrlService.init().then(loadLocales).\n then(loadUserInformation).\n then(loadUILocale);\n\n // On UI locale changes listener\n appCtrl.onChangeUILocale = function(locale) {\n appCtrl.myInfo.locale = locale;\n var uiLocaleId = appCtrl.myInfo.locale.localeId;\n if (!StringUtil.startsWith(uiLocaleId,\n LocaleService.DEFAULT_LOCALE.localeId, true)) {\n gettextCatalog.loadRemote(UrlService.uiTranslationURL(uiLocaleId))\n .then(\n function() {\n gettextCatalog.setCurrentLanguage(uiLocaleId);\n },\n function(error) {\n MessageHandler.displayInfo('Error loading UI locale. ' +\n 'Default to \\'' + LocaleService.DEFAULT_LOCALE.name +\n '\\': ' + error);\n gettextCatalog.setCurrentLanguage(\n LocaleService.DEFAULT_LOCALE);\n appCtrl.myInfo.locale = LocaleService.DEFAULT_LOCALE;\n });\n } else {\n gettextCatalog.setCurrentLanguage(\n LocaleService.DEFAULT_LOCALE.localeId);\n }\n };\n\n appCtrl.dashboardPage = function() {\n return UrlService.DASHBOARD_PAGE;\n };\n\n function loadLocales() {\n return LocaleService.getAllLocales();\n }\n\n function loadUserInformation() {\n return UserService.getMyInfo().then(\n function(myInfo) {\n appCtrl.myInfo = myInfo;\n appCtrl.myInfo.locale = LocaleService.DEFAULT_LOCALE;\n appCtrl.myInfo.gravatarUrl = UrlService.gravatarUrl(\n appCtrl.myInfo.gravatarHash, 72);\n }, function(error) {\n MessageHandler.displayError('Error loading my info: ' + error);\n });\n }\n\n function loadUILocale() {\n LocaleService.getUILocaleList().then(\n function(translationList) {\n for ( var i in translationList.locales) {\n var language = {\n 'localeId' : translationList.locales[i],\n 'name' : ''\n };\n appCtrl.uiLocaleList.push(language);\n }\n appCtrl.myInfo.locale = LocaleService.getLocaleByLocaleId(\n appCtrl.uiLocaleList, LocaleService.DEFAULT_LOCALE.localeId);\n if (!appCtrl.myInfo.locale) {\n appCtrl.myInfo.locale = LocaleService.DEFAULT_LOCALE;\n }\n },\n function(error) {\n MessageHandler.displayInfo('Error loading UI locale. ' +\n 'Default to \\'' + LocaleService.DEFAULT_LOCALE.name +\n '\\': ' + error);\n appCtrl.myInfo.locale = LocaleService.DEFAULT_LOCALE;\n });\n }\n }\n\n angular\n .module('app')\n .controller('AppCtrl', AppCtrl);\n\n})();\n\n\n\n","(function() {\n 'use strict';\n\n /**\n * EditorContentCtrl.js\n * @ngInject\n */\n function EditorContentCtrl($rootScope, EditorService, PhraseService,\n DocumentService, UrlService, EventService,\n $stateParams, PhraseUtil, $location, _,\n TransStatusService) {\n\n //TODO: move pager to directives/convert to infinite scroll\n var COUNT_PER_PAGE = 50,\n editorContentCtrl = this, status, filter;\n refreshFilterQueryFromUrl();\n\n editorContentCtrl.phrases = [];\n\n EditorService.updateContext($stateParams.projectSlug,\n $stateParams.versionSlug, DocumentService.decodeDocId($stateParams.docId),\n $stateParams.localeId);\n\n init();\n\n $rootScope.$on(EventService.EVENT.FILTER_TRANS_UNIT,\n function (event, filter) {\n if(filter.status.all === true) {\n $location.search('status', null);\n } else {\n var queries = [];\n _.forEach(filter.status, function(val, key) {\n if(val) {\n queries.push(key);\n }\n });\n $location.search('status', queries.join(','));\n }\n refreshFilterQueryFromUrl();\n init();\n });\n\n function refreshFilterQueryFromUrl() {\n status = UrlService.readValue('status');\n\n if(!_.isUndefined(status)) {\n status = status.split(',');\n status = _.transform(status, function(result, state) {\n state = TransStatusService.getServerId(state);\n return result.push(state);\n });\n }\n filter = {\n 'status': status\n };\n }\n\n\n $rootScope.$on(EventService.EVENT.GOTO_FIRST_PAGE,\n function () {\n if(EditorService.currentPageIndex > 0) {\n EditorService.currentPageIndex = 0;\n changePage(EditorService.currentPageIndex);\n }\n });\n\n $rootScope.$on(EventService.EVENT.GOTO_PREV_PAGE,\n function () {\n if(EditorService.currentPageIndex > 0) {\n EditorService.currentPageIndex -= 1;\n changePage(EditorService.currentPageIndex);\n }\n });\n\n $rootScope.$on(EventService.EVENT.GOTO_NEXT_PAGE,\n function () {\n if(EditorService.currentPageIndex < EditorService.maxPageIndex) {\n EditorService.currentPageIndex +=1;\n changePage(EditorService.currentPageIndex);\n }\n });\n\n $rootScope.$on(EventService.EVENT.GOTO_LAST_PAGE,\n function () {\n if(EditorService.currentPageIndex < EditorService.maxPageIndex) {\n EditorService.currentPageIndex = EditorService.maxPageIndex;\n changePage(EditorService.currentPageIndex);\n }\n });\n\n /*\n TODO: after moving to infinite scroll, all these go to event handler\n should move back to TransUnitService and use PhraseService.findNextId etc\n */\n // EventService.EVENT.GOTO_NEXT_ROW listener\n $rootScope.$on(EventService.EVENT.GOTO_NEXT_ROW, goToNextRow);\n\n // EventService.EVENT.GOTO_PREVIOUS_ROW listener\n $rootScope.$on(EventService.EVENT.GOTO_PREVIOUS_ROW, goToPreviousRow);\n\n // EventService.EVENT.GOTO_NEXT_UNTRANSLATED listener\n $rootScope.$on(EventService.EVENT.GOTO_NEXT_UNTRANSLATED,\n goToNextUntranslated);\n\n function goToNextRow(event, data) {\n var phrases = editorContentCtrl.phrases,\n phrase,\n currentIndex,\n nextIndex,\n nextId;\n\n currentIndex = _.findIndex(phrases, function (phrase) {\n return phrase.id === data.currentId;\n });\n nextIndex = Math.min(currentIndex + 1, phrases.length - 1);\n nextId = phrases[nextIndex].id;\n\n if (nextId !== data.currentId) {\n EventService.emitEvent(EventService.EVENT.SELECT_TRANS_UNIT,\n {\n 'id': nextId,\n 'updateURL': true,\n 'focus': true\n }, null);\n } else {\n // we have reach the end\n phrase = phrases[currentIndex];\n EventService.emitEvent(EventService.EVENT.SAVE_TRANSLATION,\n {\n 'phrase': phrase,\n 'status': PhraseUtil.getSaveButtonStatus(phrase),\n 'locale': $stateParams.localeId,\n 'docId': $stateParams.docId\n });\n }\n }\n\n function goToPreviousRow(event, data) {\n var phrases = editorContentCtrl.phrases,\n phrase,\n currentIndex,\n previousIndex,\n prevId;\n\n currentIndex = _.findIndex(phrases, function (phrase) {\n return phrase.id === data.currentId;\n });\n previousIndex = Math.max(currentIndex - 1, 0);\n prevId = phrases[previousIndex].id;\n\n if (prevId !== data.currentId) {\n EventService.emitEvent(EventService.EVENT.SELECT_TRANS_UNIT,\n {\n 'id': prevId,\n 'updateURL': true,\n 'focus': true\n }, null);\n } else {\n phrase = phrases[currentIndex];\n // have reach the start\n EventService.emitEvent(EventService.EVENT.SAVE_TRANSLATION,\n {\n 'phrase': phrase,\n 'status': PhraseUtil.getSaveButtonStatus(phrase),\n 'locale': $stateParams.localeId,\n 'docId': $stateParams.docId\n });\n }\n }\n\n function goToNextUntranslated(event, data) {\n var phrases = editorContentCtrl.phrases,\n requestStatus = TransStatusService.getStatusInfo(status),\n currentIndex,\n nextStatusInfo;\n\n currentIndex = _.findIndex(phrases, function (phrase) {\n return phrase.id === data.currentId;\n });\n\n for (var i = currentIndex + 1; i < phrases.length; i++) {\n nextStatusInfo = TransStatusService.getStatusInfo(\n phrases[i].state);\n if (nextStatusInfo.ID === requestStatus.ID) {\n EventService.emitEvent(EventService.EVENT.SELECT_TRANS_UNIT,\n {\n 'id': phrases[i].id,\n 'updateURL': true,\n 'focus': true\n }, null);\n return;\n }\n }\n // can not find next untranslated\n //TransUnitService.saveCurrentRowIfModifiedAndUnfocus(data);\n }\n\n function changePage(pageIndex) {\n loadPhrase(pageIndex);\n EventService.emitEvent(EventService.EVENT.CANCEL_EDIT);\n }\n\n /**\n * Load transUnit\n *\n * @param projectSlug\n * @param versionSlug\n * @param docId\n * @param localeId\n */\n function init() {\n EventService.emitEvent(EventService.EVENT.REFRESH_STATISTIC,\n {\n projectSlug: EditorService.context.projectSlug,\n versionSlug: EditorService.context.versionSlug,\n docId: EditorService.context.docId,\n localeId: EditorService.context.localeId\n }\n );\n\n PhraseService.getPhraseCount(EditorService.context, filter).\n then(function(count) {\n EditorService.maxPageIndex = parseInt(count / COUNT_PER_PAGE);\n if(count > COUNT_PER_PAGE) {\n EditorService.maxPageIndex = count % COUNT_PER_PAGE !== 0 ?\n EditorService.maxPageIndex +=1 : EditorService.maxPageIndex;\n }\n\n EditorService.maxPageIndex = EditorService.maxPageIndex -1 < 0 ? 0 :\n EditorService.maxPageIndex -1;\n\n loadPhrase(EditorService.currentPageIndex);\n });\n }\n\n function loadPhrase(pageIndex) {\n var startIndex = pageIndex * COUNT_PER_PAGE;\n PhraseService.fetchAllPhrase(EditorService.context, filter,\n startIndex, COUNT_PER_PAGE).then(displayPhrases);\n }\n\n function displayPhrases(phrases) {\n editorContentCtrl.phrases = phrases;\n }\n\n return editorContentCtrl;\n }\n\n angular\n .module('app')\n .controller('EditorContentCtrl', EditorContentCtrl);\n})();\n","(function() {\n 'use strict';\n\n /**\n * EditorCtrl.js\n * @ngInject\n */\n function EditorCtrl($scope, UserService, DocumentService, LocaleService,\n ProjectService, EditorService, StatisticUtil,\n UrlService, $stateParams, $state, MessageHandler, $rootScope,\n EventService, EditorShortcuts, _, Mousetrap) {\n var editorCtrl = this;\n editorCtrl.pageNumber = 1;\n editorCtrl.showCheatsheet = false;\n editorCtrl.shortcuts = _.mapValues(\n _.values(EditorShortcuts.SHORTCUTS), function(shortcutInfo) {\n // second combo (secondary keys) is an array. We have to flatten it\n var keyCombos = _.flatten(shortcutInfo.keyCombos, 'combo');\n return {\n combos: _.map(keyCombos, function(key) {\n return EditorShortcuts.symbolizeKey(key);\n }),\n description: shortcutInfo.keyCombos[0].description\n };\n });\n\n //tu status to include for display\n editorCtrl.filter = {\n 'status' : {\n 'all': true,\n 'approved' : false,\n 'translated' : false,\n 'needsWork': false,\n 'untranslated': false\n }\n };\n\n processFilterQuery();\n\n //This is just processing UI during startup,\n //phrase filtering are done in EditorContentCtrl during init\n function processFilterQuery() {\n //process filter query\n var status = UrlService.readValue('status');\n\n if(!_.isUndefined(status)) {\n status = status.split(',');\n _.forEach(status, function(val) {\n if(!_.isUndefined(editorCtrl.filter.status[val])) {\n editorCtrl.filter.status[val] = true;\n }\n });\n updateFilter();\n }\n }\n\n Mousetrap.bind('?', function(event) {\n var srcElement = event.srcElement;\n if (!editorCtrl.showCheatsheet && !stopCheatsheetCallback(srcElement)) {\n editorCtrl.toggleKeyboardShortcutsModal();\n $scope.$digest();\n }\n }, 'keyup');\n\n /**\n * Mousetrap by default stops callback on input elements BUT\n * hotkeys monkey patched it!!!\n * TODO change this hack once we remove angular hotkeys\n */\n function stopCheatsheetCallback(element) {\n // if the element has the class \"mousetrap\" then no need to stop\n if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {\n return false;\n }\n\n // stop for input, select, and textarea\n return element.tagName === 'INPUT' || element.tagName === 'SELECT' ||\n element.tagName === 'TEXTAREA' || element.isContentEditable;\n }\n\n //TODO: cross domain rest\n //TODO: Unit test\n\n //Working URL: http://localhost:8000/#/tiny-project/1/translate or\n // http://localhost:8000/#/tiny-project/1/translate/hello.txt/fr\n editorCtrl.context = EditorService.initContext($stateParams.projectSlug,\n $stateParams.versionSlug, DocumentService.decodeDocId($stateParams.docId),\n LocaleService.DEFAULT_LOCALE, LocaleService.DEFAULT_LOCALE.localeId,\n 'READ_WRITE');\n\n editorCtrl.toggleKeyboardShortcutsModal = function() {\n editorCtrl.showCheatsheet = !editorCtrl.showCheatsheet;\n };\n\n editorCtrl.versionPage = function() {\n return UrlService.PROJECT_PAGE(editorCtrl.context.projectSlug,\n editorCtrl.context.versionSlug);\n };\n\n editorCtrl.encodeDocId = function(docId) {\n return DocumentService.encodeDocId(docId);\n };\n\n ProjectService.getProjectInfo($stateParams.projectSlug).then(\n function(projectInfo) {\n editorCtrl.projectInfo = projectInfo;\n },\n function(error) {\n MessageHandler.displayError('Error getting project ' +\n 'information:' + error);\n });\n\n LocaleService.getSupportedLocales(editorCtrl.context.projectSlug,\n editorCtrl.context.versionSlug).then(\n function(locales) {\n editorCtrl.locales = locales;\n if (!editorCtrl.locales || editorCtrl.locales.length <= 0) {\n //redirect if no supported locale in version\n MessageHandler.displayError('No supported locales in ' +\n editorCtrl.context.projectSlug + ' : ' +\n editorCtrl.context.versionSlug);\n } else {\n //if localeId is not defined in url, set to first from list\n var selectedLocaleId = $state.params.localeId;\n var context = editorCtrl.context;\n\n if (!selectedLocaleId) {\n context.localeId = editorCtrl.locales[0].localeId;\n transitionToEditorSelectedView();\n } else {\n context.localeId = selectedLocaleId;\n if (!LocaleService.containsLocale(editorCtrl.locales,\n selectedLocaleId)) {\n context.localeId = editorCtrl.locales[0].localeId;\n }\n }\n }\n }, function(error) {\n MessageHandler.displayError('Error getting locale list: ' + error);\n });\n\n DocumentService.findAll(editorCtrl.context.projectSlug,\n editorCtrl.context.versionSlug).then(\n function(documents) {\n editorCtrl.documents = documents;\n\n if (!editorCtrl.documents || editorCtrl.documents.length <= 0) {\n //redirect if no documents in version\n MessageHandler.displayError('No documents in ' +\n editorCtrl.context.projectSlug + ' : ' +\n editorCtrl.context.versionSlug);\n } else {\n //if docId is not defined in url, set to first from list\n var selectedDocId = $state.params.docId,\n context = editorCtrl.context;\n if (!selectedDocId) {\n context.docId = editorCtrl.documents[0].name;\n transitionToEditorSelectedView();\n } else {\n context.docId = DocumentService.decodeDocId(selectedDocId);\n if (!DocumentService.containsDoc(editorCtrl.documents,\n context.docId)) {\n context.docId = editorCtrl.documents[0].name;\n }\n }\n }\n }, function(error) {\n MessageHandler.displayError('Error getting document list: ' + error);\n });\n\n $rootScope.$on(EventService.EVENT.SELECT_TRANS_UNIT,\n function (event, data) {\n editorCtrl.unitSelected = data.id;\n editorCtrl.focused = data.focus;\n });\n\n $rootScope.$on(EventService.EVENT.CANCEL_EDIT,\n function () {\n editorCtrl.unitSelected = false;\n editorCtrl.focused = false;\n });\n\n $rootScope.$on(EventService.EVENT.REFRESH_STATISTIC,\n function (event, data) {\n\n loadStatistic(data.projectSlug, data.versionSlug, data.docId,\n data.localeId);\n\n editorCtrl.context.docId = data.docId;\n editorCtrl.context.localeId = data.localeId;\n });\n\n editorCtrl.pageNumber = function() {\n if(EditorService.maxPageIndex === 0) {\n return EditorService.currentPageIndex + 1;\n } else {\n return (EditorService.currentPageIndex + 1) + ' of ' +\n (EditorService.maxPageIndex + 1);\n }\n };\n\n editorCtrl.getLocaleName = function(localeId) {\n return LocaleService.getName(localeId);\n };\n\n editorCtrl.firstPage = function() {\n EventService.emitEvent(EventService.EVENT.GOTO_FIRST_PAGE);\n };\n\n editorCtrl.lastPage = function() {\n EventService.emitEvent(EventService.EVENT.GOTO_LAST_PAGE);\n };\n\n\n editorCtrl.nextPage = function() {\n EventService.emitEvent(EventService.EVENT.GOTO_NEXT_PAGE);\n };\n\n editorCtrl.previousPage = function() {\n EventService.emitEvent(EventService.EVENT.GOTO_PREV_PAGE);\n };\n\n editorCtrl.resetFilter = function() {\n resetFilter(true);\n };\n\n editorCtrl.updateFilter = function() {\n updateFilter(true);\n };\n\n function updateFilter(fireEvent) {\n if(isStatusSame(editorCtrl.filter.status)) {\n resetFilter(fireEvent);\n } else {\n editorCtrl.filter.status.all = false;\n if(fireEvent) {\n EventService.emitEvent(EventService.EVENT.FILTER_TRANS_UNIT,\n editorCtrl.filter);\n }\n }\n }\n\n function resetFilter(fireEvent) {\n editorCtrl.filter.status.all = true;\n editorCtrl.filter.status.approved = false;\n editorCtrl.filter.status.translated = false;\n editorCtrl.filter.status.needsWork = false;\n editorCtrl.filter.status.untranslated = false;\n\n if(fireEvent) {\n EventService.emitEvent(EventService.EVENT.FILTER_TRANS_UNIT,\n editorCtrl.filter);\n }\n }\n\n function isStatusSame(statuses) {\n return statuses.approved === statuses.translated &&\n statuses.translated === statuses.needsWork &&\n statuses.needsWork === statuses.untranslated;\n }\n\n function transitionToEditorSelectedView() {\n if (isDocumentAndLocaleSelected()) {\n $state.go('editor.selectedContext', {\n 'docId': editorCtrl.context.docId,\n 'localeId': editorCtrl.context.localeId\n });\n }\n }\n\n function isDocumentAndLocaleSelected() {\n return editorCtrl.context.docId && editorCtrl.context.localeId;\n }\n\n /**\n * Load document statistics (word and message)\n *\n * @param projectSlug\n * @param versionSlug\n * @param docId\n * @param localeId\n */\n function loadStatistic(projectSlug, versionSlug, docId, localeId) {\n DocumentService.getStatistics(projectSlug, versionSlug, docId, localeId)\n .then(function(statistics) {\n editorCtrl.wordStatistic = StatisticUtil\n .getWordStatistic(statistics);\n editorCtrl.messageStatistic = StatisticUtil\n .getMsgStatistic(statistics);\n },\n function(error) {\n MessageHandler.displayError('Error loading statistic: ' + error);\n });\n }\n\n this.settings = UserService.settings.editor;\n\n EditorShortcuts.enableEditorKeys();\n }\n\n angular\n .module('app')\n .controller('EditorCtrl', EditorCtrl);\n})();\n","(function() {\n 'use strict';\n\n /**\n * EditorDetailsCtrl.js\n * @ngInject\n */\n function EditorDetailsCtrl() {\n var editorDetailsCtrl = this;\n\n return editorDetailsCtrl;\n }\n\n angular\n .module('app')\n .controller('EditorDetailsCtrl', EditorDetailsCtrl);\n})();\n","(function () {\n 'use strict';\n\n /**\n * EditorService.js\n * //TODO: parse editorContext in functions\n * @ngInject\n */\n function EditorService($rootScope, $resource, _, UrlService,\n EventService, PhraseService, PhraseUtil, DocumentService, MessageHandler,\n TransStatusService) {\n var editorService = this,\n queue = {};\n\n editorService.context = {};\n\n editorService.currentPageIndex = 0;\n editorService.maxPageIndex = 0;\n\n editorService.initContext =\n function (projectSlug, versionSlug, docId, srcLocale, localeId, mode) {\n editorService.context = {\n projectSlug: projectSlug,\n versionSlug: versionSlug,\n docId: docId,\n srcLocale: srcLocale,\n localeId: localeId,\n mode: mode // READ_WRITE, READ_ONLY, REVIEW\n };\n return editorService.context;\n };\n\n editorService.updateContext = function(projectSlug, versionSlug, docId,\n localeId) {\n if(editorService.context.projectSlug !== projectSlug) {\n editorService.context.projectSlug = projectSlug;\n }\n if(editorService.context.versionSlug !== versionSlug) {\n editorService.context.versionSlug = versionSlug;\n }\n if(editorService.context.docId !== docId) {\n editorService.context.docId = docId;\n }\n if(editorService.context.localeId !== localeId) {\n editorService.context.localeId = localeId;\n }\n };\n\n /**\n * EventService.EVENT.SAVE_TRANSLATION listener\n * Perform save translation with given status\n *\n * - queue save translation request (1 global queue, 1 for each TU)\n * - if queue contains request id, replace old request with new request\n */\n $rootScope.$on(EventService.EVENT.SAVE_TRANSLATION,\n function (event, data) {\n var phrase = data.phrase,\n status = data.status;\n if (!needToSavePhrase(phrase, status)) {\n // nothing has changed\n return;\n }\n\n //update pending queue if contains\n if (_.has(queue, phrase.id)) {\n var pendingRequest = queue[phrase.id];\n pendingRequest.phrase = phrase;\n pendingRequest.status = status;\n } else {\n status = resolveTranslationState(phrase, status);\n queue[phrase.id] = {\n 'phrase': phrase,\n 'status': status,\n 'locale': data.locale,\n 'docId': data.docId\n };\n }\n EventService.emitEvent(EventService.EVENT.SAVE_INITIATED, data);\n processSaveRequest(phrase.id);\n });\n\n function needToSavePhrase(phrase, status) {\n return PhraseUtil.hasTranslationChanged(phrase) ||\n phrase.status !== status;\n }\n\n // Process save translation request\n function processSaveRequest(id) {\n var context = _.cloneDeep(editorService.context);\n\n var request = queue[id];\n\n var Translation = $resource(UrlService.TRANSLATION_URL, {}, {\n update: {\n method: 'PUT',\n params: {\n localeId: request.locale\n }\n }\n });\n var data = {\n id: request.phrase.id,\n revision: request.phrase.revision || 0,\n content: request.phrase.newTranslations[0],\n contents: request.phrase.newTranslations,\n // Return status object to PascalCase Id for the server\n status: TransStatusService.getServerId(request.status.ID),\n plural: request.phrase.plural\n };\n\n Translation.update(data).$promise.then(\n function(response) {\n var oldStatus = request.phrase.status.ID;\n\n PhraseService.onTransUnitUpdated(context, data.id, request.locale,\n response.revision, response.status, request.phrase);\n\n DocumentService.updateStatistic(context.projectSlug,\n context.versionSlug, request.docId, request.locale,\n oldStatus, TransStatusService.getId(response.status),\n request.phrase.wordCount);\n\n EventService.emitEvent(EventService.EVENT.SAVE_COMPLETED,\n request.phrase);\n },\n function(response) {\n MessageHandler.displayWarning('Update translation failed for ' +\n data.id + ' -' + response);\n PhraseService.onTransUnitUpdateFailed(data.id);\n EventService.emitEvent(EventService.EVENT.SAVE_COMPLETED,\n request.phrase);\n });\n delete queue[id];\n }\n\n function resolveTranslationState(phrase, requestStatus) {\n if (_.isEmpty(_.compact(phrase.newTranslations))) {\n return TransStatusService.getStatusInfo('UNTRANSLATED');\n }\n return requestStatus;\n }\n\n return editorService;\n }\n\n angular\n .module('app')\n .factory('EditorService', EditorService);\n\n})();\n\n","(function () {\n 'use strict';\n\n /**\n * @name EditorShortcuts\n * @description service for editor keyboard shortcuts\n * @ngInject\n */\n function EditorShortcuts(EventService, $stateParams, _, hotkeys, PhraseUtil,\n TransStatusService, Mousetrap, str, $window) {\n var editorShortcuts = this,\n inSaveAsMode = false;\n\n // this will be set by TransUnitService\n // on EVENT.SELECT_TRANS_UNIT and unset on EVENT.CANCEL_EDIT\n editorShortcuts.selectedTUCtrl = null;\n\n function copySourceCallback(event) {\n if (editorShortcuts.selectedTUCtrl) {\n event.preventDefault();\n EventService.emitEvent(EventService.EVENT.COPY_FROM_SOURCE,\n {'phrase': editorShortcuts.selectedTUCtrl.getPhrase()});\n }\n }\n\n function gotoNextRowCallback(event) {\n if (editorShortcuts.selectedTUCtrl) {\n event.preventDefault();\n event.stopPropagation();\n EventService.emitEvent(EventService.EVENT.GOTO_NEXT_ROW,\n currentContext());\n }\n }\n\n function gotoPreviousRowCallback(event) {\n if (editorShortcuts.selectedTUCtrl) {\n event.preventDefault();\n event.stopPropagation();\n EventService.emitEvent(EventService.EVENT.GOTO_PREVIOUS_ROW,\n currentContext());\n }\n }\n\n function cancelEditCallback(event) {\n event.preventDefault();\n event.stopPropagation();\n if (inSaveAsMode) {\n editorShortcuts.cancelSaveAsModeIfOn();\n if (editorShortcuts.selectedTUCtrl) {\n editorShortcuts.selectedTUCtrl.focusTranslation();\n }\n } else if (editorShortcuts.selectedTUCtrl) {\n var phrase = editorShortcuts.selectedTUCtrl.getPhrase();\n if (PhraseUtil.hasTranslationChanged(phrase)) {\n // if it has changed translation, undo edit\n EventService.emitEvent(EventService.EVENT.UNDO_EDIT,\n phrase);\n } else {\n // otherwise cancel edit\n EventService.emitEvent(EventService.EVENT.CANCEL_EDIT,\n phrase);\n }\n }\n }\n\n function saveAsCurrentButtonOptionCallback(event) {\n if (editorShortcuts.selectedTUCtrl) {\n event.preventDefault();\n var phrase = editorShortcuts.selectedTUCtrl.getPhrase();\n EventService.emitEvent(EventService.EVENT.SAVE_TRANSLATION,\n {\n 'phrase': phrase,\n 'status': PhraseUtil.getSaveButtonStatus(phrase),\n 'locale': $stateParams.localeId,\n 'docId': $stateParams.docId\n });\n }\n }\n\n /**\n * This is to mimic sequence shortcut.\n * e.g. press ctlr-shift-s then press 'n' to save as\n * 'needs work'.\n */\n function saveAsModeCallback(event) {\n event.preventDefault();\n editorShortcuts.cancelSaveAsModeIfOn();\n var phrase = editorShortcuts.selectedTUCtrl.getPhrase();\n if (phrase) {\n EventService.emitEvent(EventService.EVENT.TOGGLE_SAVE_OPTIONS,\n {\n 'id': phrase.id,\n 'open': true\n });\n\n addSaveAsModeExtensionKey(phrase, 'n', 'needsWork');\n addSaveAsModeExtensionKey(phrase, 't', 'translated');\n addSaveAsModeExtensionKey(phrase, 'a', 'approved');\n }\n }\n\n /**\n * mod will be replaced by ctrl if on windows/linux or cmd if on mac.\n * By default it listens on keydown event.\n */\n editorShortcuts.SHORTCUTS = {\n COPY_SOURCE: new ShortcutInfo(\n 'alt+c', copySourceCallback, 'Copy source as translation', 'alt+g'),\n\n CANCEL_EDIT: new ShortcutInfo('esc', cancelEditCallback, 'Cancel edit'),\n\n SAVE_AS_CURRENT_BUTTON_OPTION: new ShortcutInfo(\n 'mod+s', saveAsCurrentButtonOptionCallback, 'Save'),\n\n SAVE_AS_MODE: new ShortcutInfo(\n 'mod+shift+s', saveAsModeCallback, 'Save as…'),\n\n // this is just so we can show it in cheatsheet.\n // see app/editor/EditorCtrl.shortcuts\n SAVE_AS_NEEDSWORK: {\n keyCombos: [{combo: 'mod+shift+s n', description: 'Save as needs work'}]\n },\n\n SAVE_AS_TRANSLATED: {\n keyCombos: [{combo: 'mod+shift+s t', description: 'Save as translated'}]\n },\n\n SAVE_AS_APPROVED: {\n keyCombos: [{combo: 'mod+shift+s a', description: 'Save as approved'}]\n },\n\n GOTO_NEXT_ROW_FAST: new ShortcutInfo(\n 'mod+enter', gotoNextRowCallback,\n 'Save (if changed) and go to next string',\n ['alt+k', 'alt+down']),\n\n GOTO_PREVIOUS_ROW: new ShortcutInfo(\n 'mod+shift+enter', gotoPreviousRowCallback,\n 'Save (if changed) and go to previous string',\n ['alt+j', 'alt+up'])\n /*\n Disable for now until status navigation implementation\n GOTO_NEXT_UNTRANSLATED: new ShortcutInfo(\n 'tab+u', gotoToNextUntranslatedCallback)\n */\n };\n\n /*\n Disable for now until status navigation implementation\n\n function gotoToNextUntranslatedCallback(event) {\n event.preventDefault();\n event.stopPropagation();\n if (editorShortcuts.selectedTUCtrl) {\n EventService.emitEvent(EventService.EVENT.GOTO_NEXT_UNTRANSLATED,\n currentContext());\n }\n // the shortcut is a tab + u combination\n // we don't want other tab event to trigger\n tabCombinationPressed = true;\n }\n */\n\n /**\n *\n * @param {string} defaultKey default key combo for a shortcut\n * @param {function} callback callback to execute\n * @param {string} [description]\n * optional. If not empty will apply to default key (shows in cheatsheet)\n * @param {(string|string[])} [otherKeys]\n * optional other keys that will do the same (won't show in cheatsheet)\n * @param {string} [action] optional event to listen to. e.g. 'keyup'\n * @returns {EditorShortcuts.ShortcutInfo}\n * @constructor\n */\n function ShortcutInfo(defaultKey, callback, description, otherKeys, action)\n {\n this.defaultKey = defaultKey;\n this.keyCombos = [\n singleKeyConfig(defaultKey, description, action, callback)\n ];\n if (otherKeys) {\n this.otherKeys = otherKeys instanceof Array ? otherKeys : [otherKeys];\n this.keyCombos.push(\n singleKeyConfig(this.otherKeys, '', action, callback));\n }\n return this;\n }\n\n function singleKeyConfig(combo, description, action, callback) {\n var keyConfig = {\n allowIn: ['TEXTAREA'],\n callback: callback\n };\n keyConfig.combo = combo;\n if (description) {\n keyConfig.description = description;\n }\n if (action) {\n keyConfig.action = action;\n }\n return keyConfig;\n }\n\n editorShortcuts.enableEditorKeys = function () {\n // here we only check copy source shortcut since we always enable keys in\n // bundle.\n if (!hotkeys.get(editorShortcuts.SHORTCUTS.COPY_SOURCE.defaultKey)) {\n _.forOwn(editorShortcuts.SHORTCUTS, function(value) {\n if (value instanceof ShortcutInfo) { // a hack to handle sequence keys\n enableShortcut(value);\n }\n });\n }\n };\n\n editorShortcuts.disableEditorKeys = function () {\n _.forOwn(editorShortcuts.SHORTCUTS, function(value) {\n _.forEach(value.keyCombos, function(hotkey) {\n editorShortcuts.deleteKeys(hotkey.combo, hotkey.action);\n });\n });\n };\n\n function enableShortcut(shortcutInfo) {\n if (!hotkeys.get(shortcutInfo.defaultKey)) {\n _.forEach(shortcutInfo.keyCombos,\n function(combo) {\n hotkeys.add(combo);\n });\n }\n }\n\n function currentContext() {\n return {\n 'currentId': editorShortcuts.selectedTUCtrl.getPhrase().id\n };\n }\n\n function addSaveAsModeExtensionKey(phrase, combo, status) {\n var statusInfo = TransStatusService.getStatusInfo(status);\n return hotkeys.add({\n combo: combo,\n description: str.sprintf('Save as %s', status),\n allowIn: ['INPUT', 'TEXTAREA'],\n action: 'keydown',\n callback: function (event) {\n editorShortcuts.saveTranslationCallBack(event, phrase, statusInfo);\n }\n });\n }\n\n editorShortcuts.saveTranslationCallBack = function(event, phrase,\n statusInfo) {\n inSaveAsMode = true;\n\n event.preventDefault();\n event.stopPropagation();\n\n EventService.emitEvent(EventService.EVENT.SAVE_TRANSLATION,\n {\n 'phrase': phrase,\n 'status': statusInfo,\n 'locale': $stateParams.localeId,\n 'docId': $stateParams.docId\n });\n editorShortcuts.cancelSaveAsModeIfOn();\n };\n\n editorShortcuts.cancelSaveAsModeIfOn = function() {\n if (inSaveAsMode && editorShortcuts.selectedTUCtrl) {\n inSaveAsMode = false;\n editorShortcuts.deleteKeys(['n', 't', 'a']);\n EventService.emitEvent(EventService.EVENT.TOGGLE_SAVE_OPTIONS,\n {\n 'id': editorShortcuts.selectedTUCtrl.getPhrase().id,\n 'open': false\n });\n }\n };\n\n /**\n * This is a workaround for augular-hotkeys not being able to delete hotkey.\n * @see https://github.com/chieffancypants/angular-hotkeys/issues/100\n *\n * @param {(string|string[])} keys single key or array of keys to be deleted\n * @param {string} [action='keydown'] 'keyup' or 'keydown' etc.\n */\n editorShortcuts.deleteKeys = function(keys, action) {\n var keysToDelete = keys instanceof Array ? keys : [keys];\n action = action || 'keydown';\n _.forEach(keysToDelete, function(key) {\n hotkeys.del(key);\n Mousetrap.unbind(key, action);\n });\n };\n\n /**\n * Copied from angular-hotkeys.\n * Convert strings like cmd into symbols like ⌘\n * @param {String} combo Key combination, e.g. 'mod+f'\n * @return {String} The key combination with symbols\n */\n editorShortcuts.symbolizeKey = function (combo) {\n var map = {\n command: '⌘',\n shift: '⇧',\n left: '←',\n right: '→',\n up: '↑',\n down: '↓',\n 'return': '↩',\n backspace: '⌫'\n };\n combo = combo.split('+');\n\n for (var i = 0; i < combo.length; i++) {\n // try to resolve command / ctrl based on OS:\n if (combo[i] === 'mod') {\n if ($window.navigator &&\n $window.navigator.platform.indexOf('Mac') >= 0) {\n combo[i] = 'command';\n } else {\n combo[i] = 'ctrl';\n }\n }\n\n combo[i] = map[combo[i]] || combo[i];\n }\n\n return combo.join(' + ');\n };\n\n return editorShortcuts;\n }\n\n angular\n .module('app')\n .factory('EditorShortcuts', EditorShortcuts);\n})();\n\n","(function() {\n 'use strict';\n\n /**\n * EditorSuggestionsCtrl.js\n * @ngInject\n */\n function EditorSuggestionsCtrl() {\n var editorSuggestionsCtrl = this;\n\n return editorSuggestionsCtrl;\n }\n\n angular\n .module('app')\n .controller('EditorSuggestionsCtrl', EditorSuggestionsCtrl);\n})();\n","(function() {\n 'use strict';\n\n /**\n * @name blur-on\n * @description When you put attribute 'blur-on=\"something\"',\n * you can then blur this element. It works the same way as focus-on library.\n */\n function blurOn() {\n return {\n restrict: 'A',\n link: function(scope, elem, attr) {\n return scope.$on('blurOn', function (e, name) {\n if (name === attr.blurOn) {\n return elem[0].blur();\n }\n });\n }\n };\n }\n\n angular\n .module('app')\n .directive('blurOn', blurOn);\n\n})();\n","(function() {\n 'use strict';\n\n /**\n * @name clickElsewhere\n * @description Initiate expression when clicking somewhere else\n * @ngInject\n */\n function clickElsewhere($document) {\n return {\n restrict: 'A',\n scope: {\n callback: '&clickElsewhere'\n },\n link: function(scope, element) {\n var handler = function(e) {\n if (!element[0].contains(e.target)) {\n scope.$apply(scope.callback(e));\n }\n };\n\n $document.on('click', handler);\n\n scope.$on('$destroy', function() {\n $document.off('click', handler);\n });\n }\n };\n }\n\n angular\n .module('app')\n .directive('clickElsewhere', clickElsewhere);\n\n})();\n","(function() {\n 'use strict';\n\n /**\n * Handle server communication on document related\n * information in project-version.\n *\n * DocumentService.js\n * @ngInject\n */\n function DocumentService($q, $resource, UrlService, StringUtil,\n StatisticUtil, EventService, _, TransStatusService) {\n var documentService = this,\n statisticMap = {};\n\n /**\n * Finds all documents in given project version\n *\n * @param _projectSlug\n * @param _versionSlug\n * @returns {$promise|*|N.$promise}\n */\n documentService.findAll = function findAll(_projectSlug, _versionSlug) {\n var Documents = $resource(UrlService.DOCUMENT_LIST_URL, {}, {\n query: {\n method: 'GET',\n params: {\n projectSlug: _projectSlug,\n versionSlug: _versionSlug\n },\n isArray: true\n }\n });\n return Documents.query().$promise;\n };\n\n /**\n * Get statistic of document in locale (word and message)\n *\n * @param _projectSlug\n * @param _versionSlug\n * @param _docId\n * @param _localeId\n * @returns {*}\n */\n documentService.getStatistics = function (_projectSlug, _versionSlug,\n _docId, _localeId) {\n if (_docId && _localeId) {\n var key = generateStatisticKey(_docId, _localeId);\n if (_.has(statisticMap, key)) {\n return $q.when(statisticMap[key]);\n } else {\n var encodedDocId = documentService.encodeDocId(_docId);\n var Statistics = $resource(UrlService.DOC_STATISTIC_URL, {}, {\n query: {\n method: 'GET',\n params: {\n projectSlug: _projectSlug,\n versionSlug: _versionSlug,\n docId: encodedDocId,\n localeId: _localeId\n },\n isArray: true\n }\n });\n return Statistics.query().$promise.then(function(statistics) {\n\n // Make needReview(server) available to needswork\n _.forEach(statistics, function(statistic) {\n statistic[TransStatusService.getId('needswork')] =\n statistic.needReview || 0;\n });\n\n statisticMap[key] = statistics;\n return statisticMap[key];\n });\n }\n }\n };\n\n /**\n * Encode docId, replace '/' with ',' when REST call\n * @param docId\n * @returns {*}\n */\n documentService.encodeDocId = function(docId) {\n return docId ? docId.replace(/\\//g, ',') : docId;\n };\n\n /**\n * Encode docId, replace ',' with '/' when REST call\n * @param docId\n * @returns {*}\n */\n documentService.decodeDocId = function(docId) {\n return docId ? docId.replace(/\\,/g, '/') : docId;\n };\n\n documentService.containsDoc = function (documents, docId) {\n return _.any(documents, function(document) {\n return StringUtil.equals(document.name, docId, true);\n });\n };\n\n documentService.updateStatistic = function(projectSlug, versionSlug, docId,\n localeId, oldState,\n newState, wordCount) {\n var key = generateStatisticKey(docId, localeId);\n if(_.has(statisticMap, key)) {\n adjustStatistic(statisticMap[key], oldState, newState,\n wordCount);\n\n EventService.emitEvent(EventService.EVENT.REFRESH_STATISTIC,\n {\n projectSlug: projectSlug,\n versionSlug: versionSlug,\n docId: docId,\n localeId: localeId\n }\n );\n }\n };\n\n //Generate unique key from docId and localeId for statistic cache\n function generateStatisticKey(docId, localeId) {\n return docId + '-' + localeId;\n }\n\n /**\n * Adjust statistic based on translation change of state\n * word - -wordCount of oldState, +wordCount of newState\n * msg - -1 of oldState, +1 of newState\n */\n function adjustStatistic(statistics, oldState, newState, wordCount) {\n\n var wordStatistic = StatisticUtil.getWordStatistic(statistics),\n msgStatistic = StatisticUtil.getMsgStatistic(statistics);\n\n if(wordStatistic) {\n wordCount = parseInt(wordCount);\n var wordOldState = parseInt(wordStatistic[oldState]) - wordCount;\n wordStatistic[oldState] = wordOldState < 0 ? 0 : wordOldState;\n wordStatistic[newState] = parseInt(wordStatistic[newState]) + wordCount;\n }\n\n if(msgStatistic) {\n var msgOldState = parseInt(msgStatistic[oldState]) - 1;\n msgStatistic[oldState] = msgOldState < 0 ? 0 : msgOldState;\n msgStatistic[newState] = parseInt(msgStatistic[newState]) + 1;\n }\n }\n\n return documentService;\n }\n\n angular\n .module('app')\n .factory('DocumentService', DocumentService);\n})();\n","(function() {\n 'use strict';\n\n /**\n * @name DropdownCtrl\n *\n * @description\n * Handle dropdown events between directives\n *\n * @ngInject\n */\n function DropdownCtrl($scope, $attrs, $parse, dropdownConfig,\n DropdownService, $animate, $timeout) {\n var dropdownCtrl = this,\n // create a child scope so we are not polluting original one\n scope = $scope.$new(),\n openClass = dropdownConfig.openClass,\n getIsOpen,\n setIsOpen = angular.noop,\n toggleInvoker = $attrs.onToggle ?\n $parse($attrs.onToggle) : angular.noop;\n\n this.init = function(element) {\n dropdownCtrl.$element = element;\n\n if ($attrs.isOpen) {\n getIsOpen = $parse($attrs.isOpen);\n setIsOpen = getIsOpen.assign;\n\n $scope.$watch(getIsOpen, function(value) {\n scope.isOpen = !!value;\n });\n }\n };\n\n this.toggle = function(open) {\n scope.isOpen = arguments.length ? !!open : !scope.isOpen;\n return scope.isOpen;\n };\n\n // Allow other directives to watch status\n this.isOpen = function() {\n return scope.isOpen;\n };\n\n scope.getToggleElement = function() {\n return dropdownCtrl.toggleElement;\n };\n\n scope.focusToggleElement = function() {\n if (dropdownCtrl.toggleElement) {\n dropdownCtrl.toggleElement[0].focus();\n }\n };\n\n scope.$watch('isOpen', function(isOpen, wasOpen) {\n $animate[isOpen ? 'addClass' : 'removeClass']\n (dropdownCtrl.$element, openClass);\n\n if (isOpen) {\n // need to wrap it in a timeout\n // see http://stackoverflow.com/questions/12729122/\n // prevent-error-digest-already-in-progress-when-calling-scope-apply\n $timeout(function() {\n scope.focusToggleElement();\n });\n DropdownService.open(scope);\n } else {\n DropdownService.close(scope);\n }\n\n setIsOpen($scope, isOpen);\n if (angular.isDefined(isOpen) && isOpen !== wasOpen) {\n toggleInvoker($scope, {\n open: !!isOpen\n });\n }\n });\n\n $scope.$on('$locationChangeSuccess', function() {\n scope.isOpen = false;\n });\n\n $scope.$on('$destroy', function() {\n scope.$destroy();\n });\n\n $scope.$on('openDropdown', function() {\n scope.isOpen = true;\n });\n\n $scope.$on('closeDropdown', function() {\n scope.isOpen = false;\n });\n }\n\n angular\n .module('app')\n .controller('DropdownCtrl', DropdownCtrl);\n\n})();\n","(function() {\n 'use strict';\n\n /**\n * @name dropdownService\n *\n * @description\n * Handle dropdown events between directives\n *\n * @ngInject\n */\n\n function DropdownService($document) {\n var openScope = null,\n dropdownService = this;\n\n dropdownService.open = function(dropdownScope) {\n if (!openScope) {\n $document.bind('click', closeDropdown);\n $document.bind('keydown', escapeKeyBind);\n }\n\n if (openScope && openScope !== dropdownScope) {\n openScope.isOpen = false;\n }\n\n openScope = dropdownScope;\n };\n\n dropdownService.close = function(dropdownScope) {\n if (openScope === dropdownScope) {\n openScope = null;\n $document.unbind('click', closeDropdown);\n $document.unbind('keydown', escapeKeyBind);\n }\n };\n\n var closeDropdown = function(evt) {\n if (!openScope) {\n return;\n }\n var toggleElement = openScope.getToggleElement();\n if (evt && toggleElement && toggleElement[0].contains(evt.target)) {\n return;\n }\n\n openScope.$apply(function() {\n openScope.isOpen = false;\n });\n };\n\n var escapeKeyBind = function(evt) {\n if (evt.which === 27) {\n openScope.focusToggleElement();\n closeDropdown();\n }\n };\n }\n\n angular\n .module('app')\n .service('DropdownService', DropdownService);\n\n})();\n\n","(function() {\n 'use strict';\n\n /**\n * @name Dropdown\n *\n * @description\n * Custom module for dropdowns\n *\n */\n var dropdownConfig = {\n openClass: 'is-active'\n };\n\n angular\n .module('app')\n .constant('dropdownConfig', dropdownConfig);\n\n})();\n","(function() {\n 'use strict';\n\n /**\n * @name dropdown\n *\n * @description\n * Main dropdown container\n *\n */\n\n function dropdown() {\n return {\n restrict: 'EA',\n controller: 'DropdownCtrl',\n link: function(scope, element, attrs, dropdownCtrl) {\n dropdownCtrl.init(element);\n }\n };\n }\n\n function onCloseDropdown() {\n return {\n restrict: 'A',\n require: '?^dropdown',\n scope: {\n callback: '&onCloseDropdown'\n },\n link: function(scope, elem, attrs, dropdownCtrl) {\n dropdownCtrl.onCloseDropdown = scope.callback;\n }\n };\n }\n\n /**\n * @name dropdown-toggle\n *\n * @description\n * Main dropdown toggle\n *\n */\n\n function dropdownToggle() {\n return {\n restrict: 'EA',\n require: '?^dropdown',\n link: function(scope, element, attrs, dropdownCtrl) {\n if (!dropdownCtrl) {\n return;\n }\n\n dropdownCtrl.toggleElement = element;\n\n var toggleDropdown = function(event) {\n event.preventDefault();\n event.stopPropagation();\n\n if (!element.hasClass('disabled') && !attrs.disabled) {\n scope.$apply(function() {\n dropdownCtrl.toggle();\n });\n }\n };\n\n element.bind('click', toggleDropdown);\n\n // WAI-ARIA\n element.attr({\n 'aria-haspopup': true,\n 'aria-expanded': false\n });\n scope.$watch(dropdownCtrl.isOpen, function(isOpen) {\n element.attr('aria-expanded', !!isOpen);\n if (dropdownCtrl.onCloseDropdown && !isOpen) {\n scope.$applyAsync(dropdownCtrl.onCloseDropdown);\n }\n });\n\n scope.$on('$destroy', function() {\n element.unbind('click', toggleDropdown);\n });\n }\n };\n }\n\n angular\n .module('app')\n .directive('dropdown', dropdown)\n .directive('onCloseDropdown', onCloseDropdown)\n .directive('dropdownToggle', dropdownToggle);\n\n})();\n\n","(function () {\n 'use strict';\n\n /**\n * EventService.js\n * Broadcast events service in app.\n * Usage: EventService.emitEvent(<String> event, <Map> data, scope)\n * See EventService.emitEvent\n *\n * @ngInject\n */\n function EventService($rootScope) {\n var eventService = this;\n eventService.EVENT = {\n /**\n * Loading Events\n *\n * Broadcast from AppConfig\n */\n LOADING_START: 'loadingStart',\n LOADING_STOP: 'loadingStop',\n\n /**\n * scroll to trans unit\n * data: {id: number, updateURL: boolean, focus: boolean}\n * id: (transunit id),\n * updateURL: (flag on whether to update url with trans unit id)\n * focus: flag on whether to have row in view and focused\n */\n SELECT_TRANS_UNIT: 'selectTransUnit',\n\n //data: {phrase: Phrase, sourceIndex:sourceIndex}\n COPY_FROM_SOURCE: 'copyFromSource',\n\n //data: {phrase: Phrase}\n UNDO_EDIT: 'undoEdit',\n\n //data: {phrase: Phrase}\n CANCEL_EDIT: 'cancelEdit',\n\n //data:phrase\n FOCUS_TRANSLATION: 'focusTranslation',\n\n /**\n * data: {\n * phrase: Phrase, status: StatusInfo, locale: string, docId: string\n * }\n * phrase:\n * status: Object. Request save state\n * locale: target locale\n * docId: docId\n */\n SAVE_TRANSLATION: 'saveTranslation',\n\n /**\n * Translation save in this editor is being sent to the server and\n * is waiting on a response.\n */\n SAVE_INITIATED: 'saveInitiated',\n\n /**\n * Translation save in this editor has been completed\n * (Server has responded with a success or error).\n */\n SAVE_COMPLETED: 'saveCompleted',\n\n /**\n * The text in the translation editor textbox has been edited and\n * not yet saved.\n */\n TRANSLATION_TEXT_MODIFIED: 'translationTextModified',\n\n /**\n * refresh ui statistic - changes in doc or locale\n *\n * data: {projectSlug: string, versionSlug: string,\n * docId: string, localeId: string}\n */\n REFRESH_STATISTIC: 'refreshStatistic',\n\n GOTO_PREV_PAGE: 'gotoPreviousPage',\n\n GOTO_NEXT_PAGE: 'gotoNextPage',\n\n GOTO_FIRST_PAGE: 'gotoFirstPage',\n\n GOTO_LAST_PAGE: 'gotoLastPage',\n\n /**\n * data: { currentId: number }\n */\n GOTO_NEXT_ROW: 'gotoNextRow',\n GOTO_PREVIOUS_ROW: 'gotoPreviousRow',\n GOTO_NEXT_UNTRANSLATED: 'gotoNextUntranslated',\n\n /**\n * Toggle save as options dropdown.\n * data: {id: number, open: boolean}\n */\n TOGGLE_SAVE_OPTIONS: 'openSaveOptions',\n\n\n /**\n * data: {filter: refer to editorCtrl.filter}\n */\n FILTER_TRANS_UNIT: 'filterTransUnit'\n };\n\n /**\n * Firing an event downwards of scope\n *\n * @param event - eventService.EVENT type\n * @param data - data for the event\n * @param scope - scope of event to to fire, $rootScope if empty\n */\n eventService.broadcastEvent = function(event, data, scope) {\n scope = scope || $rootScope;\n scope.$broadcast(event, data);\n };\n\n /**\n * Firing an event upwards of scope\n *\n * @param event - eventService.EVENT types\n * @param data - data for the event\n * @param scope - scope of event to to fire, $rootScope if empty\n */\n eventService.emitEvent = function(event, data, scope) {\n scope = scope || $rootScope;\n scope.$emit(event, data);\n };\n\n return eventService;\n }\n\n angular\n .module('app')\n .factory('EventService', EventService);\n})();\n","(function() {\n 'use strict';\n\n /**\n * @name icon\n * @description declarative svg icons\n * @ngInject\n */\n function icon($sce) {\n return {\n restrict: 'E',\n required: ['name'],\n scope: {\n name: '@',\n title: '@',\n size: '@'\n },\n // templateUrl: 'components/icon/icon.html',\n link: function(scope, element) {\n var svg = '',\n titleHtml = '';\n\n element.addClass('Icon');\n\n if (scope.title) {\n titleHtml = '<title>' + scope.title + '</title>';\n }\n\n // Stupid hack to make svg work\n svg = '' +\n '<svg class=\"Icon-item\">' +\n '<use xlink:href=\"#Icon-' + scope.name + '\" />' +\n titleHtml +\n '</svg>';\n element.html($sce.trustAsHtml(svg));\n }\n };\n }\n\n angular\n .module('app')\n .directive('icon', icon);\n\n})();\n","(function() {\n 'use strict';\n\n /**\n * Handle locales related information.\n *\n * LocaleService.js\n * @ngInject\n */\n function LocaleService(UrlService, StringUtil, FilterUtil, $resource, _) {\n\n var locales = [];\n\n /**\n * Get project-version supported locales\n * @param projectSlug\n * @param versionSlug\n * @returns {$promise|*}\n */\n function getSupportedLocales(projectSlug, versionSlug) {\n\n var Locales = $resource(UrlService.LOCALE_LIST_URL, {}, {\n query: {\n method: 'GET',\n params: {\n projectSlug: projectSlug,\n versionSlug: versionSlug\n },\n isArray: true\n }\n });\n\n return Locales.query().$promise;\n }\n\n //Returns all locales supported in Zanata instance\n function getAllLocales() {\n var Locales = $resource(UrlService.ALL_LOCALE_URL, {}, {\n query: {\n method: 'GET',\n isArray: true\n }\n });\n return Locales.query().$promise.then(function(results) {\n locales = FilterUtil.cleanResourceList(results);\n });\n }\n\n function getUILocaleList() {\n var list = $resource(UrlService.uiTranslationListURL, {}, {\n query: {\n method: 'GET'\n }\n });\n\n return list.query().$promise;\n }\n\n function getLocaleByLocaleId(locales, localeId) {\n if(locales) {\n return _.find(locales, function(locale) {\n return StringUtil.equals(locale.localeId, localeId, true);\n });\n }\n }\n\n function containsLocale (locales, localeId) {\n return _.any(locales, function(locale) {\n return StringUtil.equals(locale.localeId, localeId, true);\n });\n }\n\n function getName(localeId) {\n var locale = getLocaleByLocaleId(locales, localeId);\n if(locale) {\n return locale.name;\n }\n return localeId;\n }\n\n return {\n getSupportedLocales : getSupportedLocales,\n getUILocaleList : getUILocaleList,\n getLocaleByLocaleId : getLocaleByLocaleId,\n getAllLocales : getAllLocales,\n containsLocale : containsLocale,\n getName : getName,\n DEFAULT_LOCALE: {\n 'localeId' : 'en-US',\n 'name' : 'English'\n }\n };\n }\n\n angular\n .module('app')\n .factory('LocaleService', LocaleService);\n})();\n","(function() {\n\n 'use strict';\n\n /**\n * @name logoLoader\n *\n * @description\n * Logo that is activated on global loading state\n *\n * @ngInject\n */\n function logoLoader(EventService) {\n return {\n restrict: 'EA',\n scope: {\n loading: '=',\n inverted: '='\n },\n link: function(scope) {\n scope.classes = '';\n\n scope.$on(EventService.EVENT.LOADING_START, function() {\n scope.classes += ' is-loading';\n });\n\n scope.$on(EventService.EVENT.LOADING_STOP, function() {\n scope.classes = scope.classes.replace('is-loading', '');\n });\n\n scope.$watch('inverted', function(newInverted) {\n if (newInverted) {\n scope.classes += ' LogoLoader--inverted';\n } else {\n scope.classes = scope.classes.replace('LogoLoader--inverted', '');\n }\n });\n },\n templateUrl: 'components/logo-loader/logo-loader.html'\n };\n }\n\n angular\n .module('app')\n .directive('logoLoader', logoLoader);\n\n})();\n","(function() {\n 'use strict';\n\n /**\n * MessageHandler.js\n * @ngInject\n */\n function MessageHandler() {\n return {\n displayError: function(msg) {\n console.error(msg);\n },\n displayWarning: function(msg) {\n console.warn(msg);\n },\n displayInfo: function(msg) {\n console.info(msg);\n }\n };\n }\n\n angular\n .module('app')\n .factory('MessageHandler', MessageHandler);\n\n})();\n","(function() {\n 'use strict';\n\n /**\n * Handle notification in editor\n *\n * NotificationService.js\n * @ngInject\n */\n function NotificationService() {\n\n }\n\n angular\n .module('app')\n .factory('NotificationService', NotificationService);\n\n})();\n","(function () {\n 'use strict';\n\n /**\n * PhraseCache.js\n * Stores textflow, states in local cache.\n * TODO: use angular-data for storage\n * @ngInject\n */\n function PhraseCache($q, $resource, FilterUtil, UrlService, DocumentService,\n _) {\n var phraseCache = this,\n states = {}, //ids and states of all tu in order\n transUnits = {};\n\n phraseCache.getStates =\n function (projectSlug, versionSlug, documentId, localeId) {\n var key = generateKey(projectSlug, versionSlug, documentId, localeId);\n if (_.has(states, key)) {\n return $q.when(states[key]);\n } else {\n var encodedDocId = DocumentService.encodeDocId(documentId);\n var methods = {\n query: {\n method: 'GET',\n params: {\n projectSlug: projectSlug,\n versionSlug: versionSlug,\n docId: encodedDocId,\n localeId: localeId\n },\n isArray: true\n }\n },\n States = $resource(UrlService.TRANSLATION_STATUS_URL, {}, methods);\n return States.query().$promise.then(function (state) {\n state = FilterUtil.cleanResourceList(state);\n states[key] = state;\n return states[key];\n });\n }\n };\n\n phraseCache.getTransUnits = function (ids, localeId) {\n var results = {},\n missingTUId = [],\n missingLocaleTUId = [];\n ids.forEach(function (id) {\n if (_.has(transUnits, id)) {\n if(transUnits[id][localeId]) {\n results[id] = transUnits[id];\n } else {\n missingLocaleTUId.push(id);\n }\n } else {\n missingTUId.push(id);\n }\n });\n if (_.isEmpty(missingTUId) && _.isEmpty(missingLocaleTUId)) {\n return $q.when(results);\n }\n else {\n var TextFlows, Translations;\n if(!_.isEmpty(missingTUId)) {\n TextFlows = $resource(UrlService.TEXT_FLOWS_URL, {}, {\n query: {\n method: 'GET',\n params: {\n localeId: localeId,\n ids: missingTUId.join(',')\n }\n }\n });\n }\n if(!_.isEmpty(missingLocaleTUId)) {\n Translations = $resource(UrlService.TRANSLATION_URL, {}, {\n query: {\n method: 'GET',\n params: {\n localeId: localeId,\n ids: missingLocaleTUId.join(',')\n }\n }\n });\n }\n\n //need to create chain of promises\n if(TextFlows && Translations) {\n return TextFlows.query().$promise.then(updateCacheWithNewTU).\n then(Translations.query().$promise.then(updateCacheWithExistingTU));\n } else if(TextFlows) {\n return TextFlows.query().$promise.then(updateCacheWithNewTU);\n } else if(Translations) {\n return Translations.query().$promise.then(updateCacheWithExistingTU);\n }\n }\n\n function updateCacheWithExistingTU(newTransUnits) {\n newTransUnits = FilterUtil.cleanResourceMap(newTransUnits);\n for (var key in newTransUnits) {\n //push to cache\n transUnits[key][localeId] = newTransUnits[key][localeId];\n results[key] = transUnits[key]; //merge with results\n }\n return results;\n }\n\n function updateCacheWithNewTU(newTransUnits) {\n newTransUnits = FilterUtil.cleanResourceMap(newTransUnits);\n for (var key in newTransUnits) {\n transUnits[key] = newTransUnits[key]; //push to cache\n results[key] = transUnits[key]; //merge with results\n }\n return results;\n }\n };\n\n /**\n * On translation updated from server\n * @param id\n * @param localeId\n * @param revision\n * @param state\n * @param content\n * @param contents\n */\n phraseCache.onTransUnitUpdated =\n function (context, id, localeId, revision, status, phrase) {\n\n var key = generateKey(context.projectSlug, context.versionSlug,\n context.docId, localeId);\n\n var stateEntry = _.find(states[key], function(stateEntry) {\n return stateEntry.id === id;\n });\n //Update states cache\n if(stateEntry) {\n stateEntry.state = status;\n }\n\n //Update transUnits cache\n var translation = transUnits[id][localeId];\n if (!translation) {\n translation = {};\n }\n translation.revision = parseInt(revision);\n translation.state = status;\n translation.contents = phrase.newTranslations.slice();\n };\n\n function generateKey(projectId, versionId, documentId, localeId) {\n return projectId + '-' + versionId + '-' +\n documentId + '-' + localeId;\n }\n\n return phraseCache;\n }\n\n angular\n .module('app')\n .factory('PhraseCache', PhraseCache);\n\n})();\n","(function () {\n 'use strict';\n\n /**\n * @typedef {Object} Phrase\n * @property {number} id text flow id\n * @property {string[]} sources source contents\n * @property {string[]} translations original translation\n * @property {string[]} newTranslations translations in the editor\n * @property {boolean} plural whether it's in plural form\n * @property {StatusInfo} status information about this phrase\n * @property {number} revision translation revision number\n * @property {number} wordCount source word count\n */\n /**\n * @name PhraseService\n * @description Provides a list of phrases for the current document(s)\n *\n * @ngInject\n */\n function PhraseService(FilterUtil, PhraseCache, TransStatusService, _,\n $stateParams) {\n var phraseService = {};\n\n phraseService.phrases = []; //current displayed phrases\n\n // FIXME use an object for all the ID arguments - in general we will only\n // need to modify such an object sporadically when switching document\n // or locale, and it is neater than passing them all\n // around separately.\n\n phraseService.getPhraseCount = function(context, filter) {\n return PhraseCache.getStates(context.projectSlug, context.versionSlug,\n context.docId, context.localeId).then(function(states) {\n var ids = getIds(states, filter.status);\n return ids.length;\n });\n };\n\n /**\n * Fetch each of the text flows appearing in the given states data.\n */\n phraseService.fetchAllPhrase = function (context, filter,\n offset, maxResult) {\n\n var localeId = context.localeId;\n\n return PhraseCache.getStates(context.projectSlug, context.versionSlug,\n context.docId, localeId).then(getTransUnits);\n\n function getTransUnits(states) {\n var ids = getIds(states, filter.status);\n if (!isNaN(offset)) {\n if(!isNaN(maxResult)) {\n ids = ids.slice(offset, offset + maxResult);\n } else {\n ids = ids.slice(offset);\n }\n }\n // Reading for chaining promises https://github.com/kriskowal/q\n // (particularly \"Sequences\").\n return PhraseCache.getTransUnits(ids, localeId).\n then(transformToPhrases).then(sortPhrases);\n }\n\n /**\n * Converts text flow data from the API into the form expected in the\n * editor.\n *\n * @returns {Phrase[]}\n */\n function transformToPhrases(transUnits) {\n return _.map(transUnits, function(transUnit, id) {\n var source = transUnit.source,\n trans = transUnit[localeId];\n return {\n id: parseInt(id),\n sources: source.plural ? source.contents : [source.content],\n // Original translation\n translations: extractTranslations(source, trans),\n // Translation from editor\n newTranslations: extractTranslations(source, trans),\n plural: source.plural,\n // Conform the status from the server, return an object\n status: trans ? TransStatusService.getStatusInfo(trans.state) :\n TransStatusService.getStatusInfo('untranslated'),\n revision: trans ? parseInt(trans.revision) : 0,\n wordCount: parseInt(source.wordCount)\n };\n });\n }\n\n function extractTranslations(source, trans) {\n if(source.plural) {\n return trans && trans.contents ? trans.contents.slice() : [];\n }\n return trans ? [trans.content] : [];\n }\n\n function sortPhrases(phrases) {\n return PhraseCache.getStates(context.projectSlug, context.versionSlug,\n context.docId, localeId).then(function(states) {\n phraseService.phrases = _.sortBy(phrases, function(phrase) {\n var index = _.findIndex(states, function(state) {\n return state.id === phrase.id;\n });\n return index >= 0 ? index : phrases.length;\n });\n return phraseService.phrases;\n });\n }\n };\n\n //update phrase,statuses and textFlows with given tu id\n phraseService.onTransUnitUpdated = function(context, id, localeId, revision,\n status, phrase) {\n\n PhraseCache.onTransUnitUpdated(context, id, localeId, revision, status,\n phrase);\n\n var cachedPhrase = findPhrase(id, phraseService.phrases);\n //update phrase if found\n if(cachedPhrase) {\n cachedPhrase.translations = phrase.newTranslations.slice();\n cachedPhrase.revision = revision;\n cachedPhrase.status = TransStatusService.getStatusInfo(status);\n }\n };\n\n //rollback content of phrase\n phraseService.onTransUnitUpdateFailed = function(id) {\n var phrase = findPhrase(id, phraseService.phrases);\n if(phrase) {\n phrase.newTranslations = phrase.translations.slice();\n }\n };\n\n // find next Id from phrases states\n phraseService.findNextId = function(currentId) {\n return PhraseCache.getStates($stateParams.projectSlug,\n $stateParams.versionSlug, $stateParams.docId,\n $stateParams.localeId)\n .then(function (states) {\n var currentIndex,\n nextIndex;\n currentIndex = _.findIndex(states, function (state) {\n return state.id === currentId;\n });\n nextIndex = currentIndex + 1 < states.length ?\n currentIndex + 1 : states.length - 1;\n return states[nextIndex].id;\n });\n };\n\n // find previous id from phrases states\n phraseService.findPreviousId = function(currentId) {\n return PhraseCache.getStates($stateParams.projectSlug,\n $stateParams.versionSlug, $stateParams.docId,\n $stateParams.localeId)\n .then(function (states) {\n var currentIndex,\n previousIndex;\n currentIndex = _.findIndex(states, function (state) {\n return state.id === currentId;\n });\n previousIndex = currentIndex - 1 >= 0 ? currentIndex - 1 : 0;\n return states[previousIndex].id;\n });\n };\n\n // find next phrase with requested status\n phraseService.findNextStatus = function(currentId, status) {\n return PhraseCache.getStates($stateParams.projectSlug,\n $stateParams.versionSlug, $stateParams.docId,\n $stateParams.localeId)\n .then(function (statusList) {\n var currentIndex,\n nextStatusInfo,\n requestStatus = TransStatusService.getStatusInfo(status);\n\n currentIndex = _.findIndex(statusList, function (state) {\n return state.id === currentId;\n });\n\n for (var i = currentIndex + 1; i < statusList.length; i++) {\n nextStatusInfo = TransStatusService.getStatusInfo(\n statusList[i].state);\n if (nextStatusInfo.ID === requestStatus.ID) {\n return statusList[i].id;\n }\n }\n return currentId;\n });\n };\n\n function findPhrase(id, phrases) {\n return _.find(phrases, function(phrase) {\n return phrase.id === id;\n });\n }\n\n function getIds(resources, states) {\n if(states) {\n resources = FilterUtil.filterResources(resources, ['status'], states);\n }\n return _.map(resources, function (item) {\n return item.id;\n });\n }\n\n // Does not appear to be used anywhere. Removing until phrase-caching code\n // is added.\n // phraseService.findById = function(phraseId) {\n // var deferred = $q.defer();\n // var phrase = phrases[phraseId];\n // deferred.resolve(phrase);\n // return deferred.promise;\n // };\n\n return phraseService;\n }\n\n angular\n .module('app')\n .factory('PhraseService', PhraseService);\n\n})();\n\n","(function() {\n 'use strict';\n\n /**\n * @name progressbar\n * @description progressbar container\n * @ngInject\n */\n function progressbar() {\n return {\n restrict: 'E',\n required: 'progressbarStatistic',\n scope: {\n statistic: '=progressbarStatistic',\n size: '@' //large, full, or empty\n },\n templateUrl: 'components/progressbar/progressbar.html',\n controller: function($scope) {\n /**\n * Need to set to true for complex object watch. Performance issue.\n * https://docs.angularjs.org/api/ng/type/$rootScope.Scope\n */\n $scope.$watch('statistic', function(statistic) {\n if (statistic) {\n $scope.style = getStyle(statistic);\n }\n }, true);\n }\n };\n }\n\n function getStyle(statistic) {\n var total = statistic.total,\n widthApproved = getWidthPercent(statistic.approved, total),\n widthTranslated = getWidthPercent(statistic.translated, total),\n marginLeftTranslated = widthApproved,\n widthNeedsWork = getWidthPercent(statistic.needswork, total),\n marginLeftNeedsWork = widthApproved + widthTranslated,\n widthUntranslated = getWidthPercent(statistic.untranslated, total),\n marginLeftUntranslated = widthApproved +\n widthTranslated + widthNeedsWork,\n style = {};\n\n style.approved = {\n 'width': widthApproved + '%',\n 'marginLeft': 0\n };\n style.translated = {\n 'width': widthTranslated + '%',\n 'marginLeft': marginLeftTranslated + '%'\n };\n style.needsWork = {\n 'width': widthNeedsWork + '%',\n 'marginLeft': marginLeftNeedsWork + '%'\n };\n style.untranslated = {\n 'width': widthUntranslated + '%',\n 'marginLeft': marginLeftUntranslated + '%'\n };\n return style;\n }\n\n function getWidthPercent(value, total) {\n var percent = 0;\n if (value) {\n percent = value / total * 100;\n }\n return percent;\n }\n\n angular\n .module('app')\n .directive('progressbar', progressbar);\n\n})();\n","(function() {\n 'use strict';\n\n /**\n * Handle communication with server on Project related information.\n * ProjectService.js\n * @ngInject\n */\n\n function ProjectService(UrlService, $resource) {\n\n /**\n * Get project's information\n *\n * @param projectSlug\n * @returns {$promise|*|N.$promise}\n */\n function getProjectInfo(projectSlug) {\n var methods = {\n query: {\n method: 'GET',\n params: {\n projectSlug: projectSlug\n }\n }\n };\n\n var Locales = $resource(UrlService.PROJECT_URL, {}, methods);\n return Locales.query().$promise;\n }\n\n return {\n getProjectInfo: getProjectInfo\n };\n }\n angular\n .module('app')\n .factory('ProjectService', ProjectService);\n})();\n","(function() {\n 'use strict';\n\n /**\n * @name display-character\n * @description display whitespace character with symbol(HTML),\n * *NOTE*, need to wrap around <pre> tag\n * @ngInject\n */\n function renderWhitespaceCharacters() {\n var WHITESPACES = {\n 'space' : {\n 'regex' : / /g,\n 'template' : '<span class=\"u-textSpace\"> </span>'\n },\n 'newline' : {\n 'regex' : /\\n/g,\n 'template' : '<span class=\"u-textPilcrow\"></span>\\n'\n },\n 'tab' : {\n 'regex' : /\\t/g,\n 'template' : '<span class=\"u-textTab\">\\t</span>'\n }\n };\n\n return {\n restrict: 'A',\n required: ['ngBind'],\n scope: {\n ngBind: '='\n },\n\n link: function compile(scope, element) {\n scope.$watch('ngBind', function (value) {\n value = replaceChar(value, WHITESPACES.space);\n value = replaceChar(value, WHITESPACES.newline);\n value = replaceChar(value, WHITESPACES.tab);\n element.html(value);\n });\n }\n };\n\n function replaceChar(value, whitespaceChar) {\n return value.replace(whitespaceChar.regex, whitespaceChar.template);\n }\n }\n\n angular\n .module('app')\n .directive('renderWhitespaceCharacters', renderWhitespaceCharacters);\n\n})();\n","(function() {\n 'use strict';\n\n /**\n * @name ScrollbarWidthCtrl\n *\n * @description\n * Handle dropdown events between directives\n *\n * @ngInject\n */\n function ScrollbarWidthCtrl() {\n var scrollbarWidthCtrl = this;\n\n scrollbarWidthCtrl.init = function() {\n var container = scrollbarWidthCtrl.container[0],\n child = scrollbarWidthCtrl.child[0],\n scrollbarWidth = child.offsetWidth - container.offsetWidth;\n\n scrollbarWidthCtrl.width = scrollbarWidth / 2;\n };\n\n }\n\n angular\n .module('app')\n .controller('ScrollbarWidthCtrl', ScrollbarWidthCtrl);\n\n})();\n","(function() {\n 'use strict';\n\n /**\n * @name scrollbarWidth\n *\n * @description\n * Scrollbar width container\n * Needed for the controller to reference all properties\n */\n function scrollbarWidth() {\n return {\n restrict: 'A',\n controller: 'ScrollbarWidthCtrl as scrollbarWidthCtrl',\n link: function(scope, element, attrs, scrollbarWidthCtrl) {\n scrollbarWidthCtrl.init(element);\n }\n };\n }\n\n /**\n * @name scrollbarWidthElement\n *\n * @description\n * The element to add the scrollbar width to\n */\n function scrollbarWidthElement() {\n return {\n restrict: 'A',\n require: '?^scrollbarWidth',\n link: function(scope, element, attrs, scrollbarWidthCtrl) {\n if (!scrollbarWidthCtrl) {\n return;\n }\n // Use the attribute to decide which property to set\n element.css(attrs.scrollbarWidthElement, scrollbarWidthCtrl.width);\n }\n };\n }\n\n /**\n * @name scrollbarWidthContainer\n *\n * @description\n * Get the scrollbar container width\n */\n function scrollbarWidthContainer() {\n return {\n restrict: 'A',\n require: '?^scrollbarWidth',\n link: function(scope, element, attrs, scrollbarWidthCtrl) {\n if (!scrollbarWidthCtrl) {\n return;\n }\n scrollbarWidthCtrl.container = element;\n }\n };\n }\n\n /**\n * @name scrollbarWidthChild\n *\n * @description\n * Get the scrollbar child width\n */\n function scrollbarWidthChild() {\n return {\n restrict: 'A',\n require: '?^scrollbarWidth',\n link: function(scope, element, attrs, scrollbarWidthCtrl) {\n if (!scrollbarWidthCtrl) {\n return;\n }\n scrollbarWidthCtrl.child = element;\n }\n };\n }\n\n angular\n .module('app')\n .directive('scrollbarWidth', scrollbarWidth)\n .directive('scrollbarWidthElement', scrollbarWidthElement)\n .directive('scrollbarWidthContainer', scrollbarWidthContainer)\n .directive('scrollbarWidthChild', scrollbarWidthChild);\n\n})();\n","(function() {\n 'use strict';\n\n /**\n * @name toggle-checkbox\n * @description Add an extra element to a checkbox to\n * so we can style it differently\n * @ngInject\n */\n function toggleCheckbox() {\n return {\n restrict: 'A',\n link: function(scope, element) {\n element.after('<span class=\"Toggle-fakeCheckbox\"></span>');\n }\n };\n }\n\n angular\n .module('app')\n .directive('toggleCheckbox', toggleCheckbox);\n\n})();\n","(function () {\n 'use strict';\n\n /**\n * @typedef {Object} StatusInfo\n * @property {string} ID lower case translation status (content state)\n * @property {string} NAME capitalized representation\n * @property {string} CSSCLASS css class to use for this status\n *\n */\n /**\n * TransStatusService.js\n *\n * @ngInject\n */\n function TransStatusService(_) {\n var transStatusService = this,\n STATUSES = {\n 'UNTRANSLATED': {\n 'ID': 'untranslated',\n 'NAME': 'Untranslated',\n 'CSSCLASS': 'neutral'\n },\n 'NEEDSWORK': {\n 'ID': 'needswork',\n 'NAME': 'Needs Work',\n 'CSSCLASS': 'unsure'\n },\n 'TRANSLATED' : {\n 'ID': 'translated',\n 'NAME': 'Translated',\n 'CSSCLASS': 'success'\n },\n 'APPROVED': {\n 'ID': 'approved',\n 'NAME': 'Approved',\n 'CSSCLASS': 'highlight'\n }\n };\n\n transStatusService.getAll = function() {\n return STATUSES;\n };\n\n transStatusService.getAllAsArray = function() {\n return _.values(STATUSES);\n };\n\n /**\n *\n * @param {string} statusKey string representation of the status.\n * @returns {StatusInfo}\n */\n transStatusService.getStatusInfo = function(statusKey) {\n return STATUSES[conformStatus(statusKey)];\n };\n\n transStatusService.getId = function(statusKey) {\n return STATUSES[conformStatus(statusKey)].ID;\n };\n\n transStatusService.getServerId = function(statusId) {\n return serverStatusId(statusId);\n };\n\n transStatusService.getName = function(statusKey) {\n return STATUSES[conformStatus(statusKey)].NAME;\n };\n\n transStatusService.getCSSClass = function(statusKey) {\n return STATUSES[conformStatus(statusKey)].CSSCLASS;\n };\n\n /**\n * Conform it to uppercase for lookups and\n * temporary fix for server sending \"needReview\"\n * instead of needswork status\n * @param {string} status\n * @return {string} new value to use\n */\n function conformStatus(statusKey) {\n statusKey = angular.uppercase(statusKey);\n if (!statusKey || statusKey === 'NEW') {\n statusKey = 'UNTRANSLATED';\n } else if (statusKey === 'NEEDREVIEW') {\n statusKey = 'NEEDSWORK';\n }\n return statusKey;\n }\n\n /**\n * Conform it to PascalCase for lookups and\n * temporary fix for server receiving \"needReview\"\n * instead of needswork status\n * @param {string} status\n * @return {string} new value to use\n */\n function serverStatusId(statusId) {\n statusId = angular.lowercase(statusId);\n if (!statusId || statusId === 'untranslated') {\n return 'New';\n } else if (statusId === 'needswork') {\n return 'NeedReview';\n }\n return statusId.charAt(0).toUpperCase() + statusId.slice(1).toLowerCase();\n }\n\n return transStatusService;\n }\n\n angular\n .module('app')\n .factory('TransStatusService', TransStatusService);\n})();\n\n","(function () {\n 'use strict';\n\n /**\n * TransUnitCtrl.js\n * @ngInject\n */\n function TransUnitCtrl($scope, $element, $stateParams, _,\n TransUnitService, EventService, LocaleService, focus,\n EditorShortcuts, PhraseUtil) {\n\n var transUnitCtrl = this;\n\n transUnitCtrl.selected = false;\n transUnitCtrl.focused = false;\n transUnitCtrl.focusedTranslationIndex = 0;\n\n transUnitCtrl.hasTranslationChanged =\n PhraseUtil.hasTranslationChanged;\n\n transUnitCtrl.focusTranslation = function() {\n if(transUnitCtrl.selected) {\n focus('phrase-' + $scope.phrase.id + '-' +\n transUnitCtrl.focusedTranslationIndex);\n }\n };\n\n // when user clicked on TU or using tab to nav\n transUnitCtrl.onTextAreaFocus = function(phrase, index) {\n transUnitCtrl.focused = true;\n if (!_.isUndefined(index)) {\n transUnitCtrl.focusedTranslationIndex = index;\n }\n if(!transUnitCtrl.selected) {\n EventService.emitEvent(EventService.EVENT.SELECT_TRANS_UNIT,\n {'id': phrase.id,\n 'updateURL': true,\n 'focus': true\n }, $scope);\n }\n };\n\n transUnitCtrl.translationTextModified = function(phrase) {\n EventService.emitEvent(EventService.EVENT.TRANSLATION_TEXT_MODIFIED,\n phrase);\n };\n\n transUnitCtrl.getPhrase = function() {\n return $scope.phrase;\n };\n\n transUnitCtrl.init = function() {\n TransUnitService.addController($scope.phrase.id, transUnitCtrl);\n if($stateParams.id && parseInt($stateParams.id) === $scope.phrase.id) {\n EventService.emitEvent(EventService.EVENT.SELECT_TRANS_UNIT,\n {'id': $stateParams.id,\n 'updateURL': false,\n 'focus' : $stateParams.selected});\n }\n };\n\n transUnitCtrl.copySource = function($event, phrase, sourceIndex) {\n $event.stopPropagation(); //prevent click event of TU\n EventService.emitEvent(EventService.EVENT.COPY_FROM_SOURCE,\n {'phrase': phrase, 'sourceIndex': sourceIndex}, $scope);\n };\n\n transUnitCtrl.undoEdit = function($event, phrase) {\n $event.stopPropagation(); //prevent click event of TU\n EventService.emitEvent(EventService.EVENT.UNDO_EDIT,\n phrase, $scope);\n };\n\n transUnitCtrl.cancelEdit = function($event, phrase) {\n $event.stopPropagation(); //prevent click event of TU\n EventService.emitEvent(EventService.EVENT.CANCEL_EDIT,\n phrase, $scope);\n };\n\n transUnitCtrl.saveAs = function($event, phrase, status) {\n EditorShortcuts.saveTranslationCallBack($event, phrase, status);\n };\n\n transUnitCtrl.getLocaleName = function(localeId) {\n return LocaleService.getName(localeId);\n };\n\n transUnitCtrl.toggleSaveAsOptions = function(open) {\n EventService.broadcastEvent( open ? 'openDropdown': 'closeDropdown',\n {}, $scope);\n if (open) {\n // focus on the first dropdown option\n focus($scope.phrase.id + '-saveAsOption-0');\n }\n };\n\n transUnitCtrl.cancelSaveAsMode = function() {\n EditorShortcuts.cancelSaveAsModeIfOn();\n };\n\n $scope.$on('$destroy', function () {\n $element.unbind('click', onTransUnitClick);\n $element.unbind('focus', onTransUnitClick);\n });\n\n transUnitCtrl.updateSaveButton = function (phrase) {\n transUnitCtrl.saveButtonStatus =\n PhraseUtil.getSaveButtonStatus($scope.phrase);\n transUnitCtrl.saveButtonOptions =\n TransUnitService.getSaveButtonOptions(transUnitCtrl.saveButtonStatus,\n $scope.phrase);\n transUnitCtrl.saveButtonText = transUnitCtrl.saveButtonStatus.NAME;\n transUnitCtrl.saveButtonDisabled =\n !PhraseUtil.hasTranslationChanged(phrase);\n transUnitCtrl.loadingClass = '';\n transUnitCtrl.savingStatus = '';\n };\n\n transUnitCtrl.phraseSaving = function (data) {\n transUnitCtrl.loadingClass = 'is-loading';\n transUnitCtrl.saveButtonStatus =\n transUnitCtrl.savingStatus = data.status;\n transUnitCtrl.saveButtonOptions =\n TransUnitService.getSaveButtonOptions(transUnitCtrl.saveButtonStatus,\n data.phrase);\n transUnitCtrl.saveButtonText = 'Saving…';\n transUnitCtrl.saveButtonDisabled = true;\n };\n\n transUnitCtrl.saveButtonOptionsAvailable = function() {\n return !_.isEmpty(transUnitCtrl.saveButtonOptions);\n };\n\n transUnitCtrl.selectTransUnit = function(phrase) {\n if (!transUnitCtrl.selected) {\n EventService.emitEvent(EventService.EVENT.SELECT_TRANS_UNIT,\n {'id': phrase.id,\n 'updateURL': true,\n 'focus': true\n }, $scope);\n }\n };\n\n function onTransUnitClick() {\n if(!transUnitCtrl.selected) {\n $scope.$apply(function () {\n EventService.emitEvent(EventService.EVENT.SELECT_TRANS_UNIT,\n {'id': $scope.phrase.id,\n 'updateURL': true,\n 'focus': true}, $scope);\n });\n }\n }\n\n return transUnitCtrl;\n }\n\n angular\n .module('app')\n .controller('TransUnitCtrl', TransUnitCtrl);\n})();\n\n","(function () {\n 'use strict';\n\n /**\n * TransUnitService\n *\n * See PhraseService.transformToPhrases function for phrase definition.\n *\n * @ngInject\n */\n function TransUnitService(_, $location, $rootScope, $state, $stateParams,\n $filter, MessageHandler, EventService, TransStatusService, PRODUCTION,\n EditorShortcuts, PhraseUtil, $timeout) {\n var transUnitService = this,\n controllerList = {},\n selectedTUId;\n\n transUnitService.addController = function(id, controller) {\n controllerList[id] = controller;\n };\n\n transUnitService.getSaveButtonOptions = function(saveButtonStatus, phrase) {\n return filterSaveButtonOptions(saveButtonStatus, phrase);\n };\n\n $rootScope.$on(EventService.EVENT.TOGGLE_SAVE_OPTIONS,\n function(event, data) {\n var transUnitCtrl = controllerList[data.id];\n if (transUnitCtrl) {\n transUnitCtrl.toggleSaveAsOptions(data.open);\n }\n });\n\n /**\n * EventService.EVENT.SELECT_TRANS_UNIT listener\n * - Select and focus a trans-unit.\n * - Perform implicit save on previous selected TU if changed\n * - Update url with TU id without reload state\n */\n $rootScope.$on(EventService.EVENT.SELECT_TRANS_UNIT,\n function (event, data) {\n var newTuController = controllerList[data.id],\n oldTUController = controllerList[selectedTUId],\n updateURL = data.updateURL;\n\n if(newTuController) {\n EditorShortcuts.selectedTUCtrl = newTuController;\n\n if (selectedTUId && selectedTUId !== data.id) {\n setSelected(oldTUController, false);\n\n //perform implicit save if changed\n if(PhraseUtil.hasTranslationChanged(\n oldTUController.getPhrase())) {\n EventService.emitEvent(EventService.EVENT.SAVE_TRANSLATION,\n {\n 'phrase' : oldTUController.getPhrase(),\n 'status' : TransStatusService.getStatusInfo('TRANSLATED'),\n 'locale' : $stateParams.localeId,\n 'docId' : $stateParams.docId\n });\n }\n }\n\n updateSaveButton(event, newTuController.getPhrase());\n selectedTUId = data.id;\n setSelected(newTuController, true);\n\n EventService.emitEvent(EventService.EVENT.FOCUS_TRANSLATION, data);\n\n //Update url without reload state\n if(updateURL) {\n if($state.current.name !== 'editor.selectedContext.tu') {\n $state.go('editor.selectedContext.tu', {\n 'id': data.id,\n 'selected': data.focus.toString()\n });\n } else {\n $location.search('id', data.id);\n $location.search('selected', data.focus.toString());\n }\n }\n } else {\n MessageHandler.displayWarning('Trans-unit not found:' + data.id);\n }\n });\n\n /**\n * EventService.EVENT.COPY_FROM_SOURCE listener\n * Copy translation from source\n */\n $rootScope.$on(EventService.EVENT.COPY_FROM_SOURCE,\n function (event, data) {\n var sourceIndex = 0;\n if(data.phrase.plural) {\n //clicked copy source button\n sourceIndex = data.sourceIndex;\n if(_.isUndefined(sourceIndex)) {\n //copy source key shortcut, copy corresponding source to target\n var transUnitCtrl = controllerList[data.phrase.id];\n sourceIndex = transUnitCtrl.focusedTranslationIndex;\n if(data.phrase.sources.length <\n transUnitCtrl.focusedTranslationIndex + 1) {\n sourceIndex = data.phrase.sources.length - 1;\n }\n }\n }\n setTranslationText(data.phrase, data.phrase.sources[sourceIndex]);\n });\n\n /**\n * EventService.EVENT.UNDO_EDIT listener\n * Cancel edit and restore translation\n */\n $rootScope.$on(EventService.EVENT.UNDO_EDIT,\n function (event, phrase) {\n if (PhraseUtil.hasTranslationChanged(phrase)) {\n setAllTranslations(phrase, phrase.translations);\n }\n });\n\n /**\n * EventService.EVENT.CANCEL_EDIT listener\n * Cancel edit and restore translation\n */\n $rootScope.$on(EventService.EVENT.CANCEL_EDIT,\n function (event, phrase) {\n if(selectedTUId) {\n setSelected(controllerList[selectedTUId], false);\n selectedTUId = false;\n EditorShortcuts.selectedTUCtrl = null;\n }\n\n $location.search('selected', null);\n if(!phrase) {\n $location.search('id', null);\n }\n\n // EditorContentCtrl#changePage doesn't provide a phrase object\n if (phrase) {\n $timeout(function() {\n return $rootScope.$broadcast('blurOn', 'phrase-' + phrase.id);\n });\n }\n });\n\n /**\n * EventService.EVENT.TRANSLATION_TEXT_MODIFIED listener\n *\n */\n $rootScope.$on(EventService.EVENT.TRANSLATION_TEXT_MODIFIED,\n updateSaveButton);\n\n /**\n * EventService.EVENT.FOCUS_TRANSLATION listener\n *\n */\n $rootScope.$on(EventService.EVENT.FOCUS_TRANSLATION,\n setFocus);\n\n /**\n * EventService.EVENT.SAVE_COMPLETED listener\n *\n */\n $rootScope.$on(EventService.EVENT.SAVE_INITIATED,\n phraseSaving);\n\n /**\n * EventService.EVENT.SAVE_COMPLETED listener\n *\n */\n $rootScope.$on(EventService.EVENT.SAVE_COMPLETED,\n updateSaveButton);\n\n function setTranslationText(phrase, newText) {\n var index = 0;\n if (phrase.plural) {\n var transUnitCtrl = controllerList[phrase.id];\n index = transUnitCtrl.focusedTranslationIndex;\n }\n phrase.newTranslations[index] = newText;\n EventService.emitEvent(EventService.EVENT.TRANSLATION_TEXT_MODIFIED,\n phrase);\n EventService.emitEvent(EventService.EVENT.FOCUS_TRANSLATION,\n phrase);\n }\n\n function setAllTranslations(phrase, newTexts) {\n //need slice() for new instance of array\n phrase.newTranslations = newTexts.slice();\n\n EventService.emitEvent(EventService.EVENT.TRANSLATION_TEXT_MODIFIED,\n phrase);\n EventService.emitEvent(EventService.EVENT.FOCUS_TRANSLATION,\n phrase);\n }\n\n function updateSaveButton(event, phrase) {\n var transUnitCtrl = controllerList[phrase.id];\n transUnitCtrl.updateSaveButton(phrase);\n }\n\n function phraseSaving(event, data) {\n var transUnitCtrl = controllerList[data.phrase.id];\n transUnitCtrl.phraseSaving(data);\n EventService.emitEvent(EventService.EVENT.FOCUS_TRANSLATION,\n data.phrase);\n }\n\n function setSelected(transUnitCtrl, isSelected) {\n //This check is to prevent selected event being triggered repeatedly.\n if(transUnitCtrl.selected !== isSelected) {\n transUnitCtrl.selected = isSelected || false;\n }\n }\n\n function setFocus(event, phrase) {\n var transUnitCtrl = controllerList[phrase.id];\n transUnitCtrl.focusTranslation();\n }\n\n /**\n * Filters the dropdown options for saving a translation\n * Unless the translation is empty, remove untranslated as an option\n * Filter the current default save state out of the list and show remaining\n *\n * @param {Object} saveStatus The current default translation *save* status\n * @return {Array} Is used to construct the dropdown list\n */\n function filterSaveButtonOptions(saveStatus, phrase) {\n var filteredOptions = [];\n if (saveStatus.ID === 'untranslated') {\n return filteredOptions;\n }\n filteredOptions = $filter('filter')\n (TransStatusService.getAllAsArray(), {ID: '!untranslated'});\n\n if(phrase.plural) {\n if(PhraseUtil.hasNoTranslation(phrase)) {\n filteredOptions = $filter('filter')\n (filteredOptions, {ID: '!needswork'});\n } else if(PhraseUtil.hasEmptyTranslation(phrase)) {\n filteredOptions = $filter('filter')\n (filteredOptions, {ID: '!translated'});\n }\n }\n\n if (PRODUCTION) {\n filteredOptions = $filter('filter')\n (filteredOptions, {ID: '!approved'});\n }\n\n return $filter('filter')(filteredOptions, {ID: '!'+saveStatus.ID});\n }\n\n return transUnitService;\n }\n\n angular\n .module('app')\n .factory('TransUnitService', TransUnitService);\n})();\n\n\n","(function() {\n 'use strict';\n\n /**\n * @name trans-unit\n * @description transUnit container\n * @ngInject\n */\n function transUnit() {\n return {\n restrict: 'E',\n required: ['phrase', 'editorContext'],\n scope: {\n phrase: '=',\n firstPhrase: '=',\n editorContext: '='\n },\n controller: 'TransUnitCtrl as transUnitCtrl',\n templateUrl: 'components/transUnit/trans-unit.html',\n link: function(scope, element, attr, TransUnitCtrl) {\n TransUnitCtrl.init();\n }\n };\n }\n\n angular\n .module('app')\n .directive('transUnit', transUnit);\n\n})();\n","(function() {\n 'use strict';\n\n /**\n * @name trans-unit\n * @description transUnit container\n * @ngInject\n */\n function transUnitFilter() {\n return {\n restrict: 'E',\n required: ['editor'],\n scope: {\n editor: '='\n },\n templateUrl: 'components/transUnitFilter/trans-unit-filter.html'\n };\n }\n\n angular\n .module('app')\n .directive('transUnitFilter', transUnitFilter);\n\n})();\n","(function() {\n 'use strict';\n\n /**\n * UserService.js\n *\n * @ngInject\n */\n function UserService($resource, UrlService) {\n\n function getUserInfo(username) {\n var UserInfo = $resource(UrlService.USER_INFO_URL, {}, {\n query: {\n method: 'GET',\n params: {\n username: username\n }\n }\n });\n return UserInfo.query().$promise;\n }\n\n function getMyInfo() {\n var MyInfo = $resource(UrlService.MY_INFO_URL, {}, {\n query: {\n method: 'GET'\n }\n });\n return MyInfo.query().$promise;\n }\n\n return {\n settings: {\n editor: {\n hideMainNav: false\n }\n },\n getUserInfo: getUserInfo,\n getMyInfo: getMyInfo\n };\n }\n angular\n .module('app')\n .factory('UserService', UserService);\n})();\n","(function() {\n 'use strict';\n\n /**\n * FilterUtil.js\n *\n * @ngInject\n */\n function FilterUtil(StringUtil, _) {\n\n /**\n * Filter in resources on given fields with matched terms\n *\n * @param resources - list of resources\n * @param fields - list of fields to check\n * @param terms - list of term to check\n * @returns {*}\n */\n function filterResources(resources, fields, terms) {\n if(!resources || !fields || !terms) {\n return resources;\n }\n return _.filter(resources, function (resource) {\n return isInclude(resource, fields, terms);\n });\n }\n\n /**\n * Filter out properties starting with $ (added by promise)\n * @param resources\n */\n function cleanResourceMap(resources) {\n var filteredList = {};\n var ids = Object.keys(resources).filter(function (id) {\n return id.indexOf('$') === -1;\n });\n ids.forEach(function(id) {\n filteredList[id] = (resources[id]);\n });\n return filteredList;\n }\n\n function cleanResourceList(resources) {\n var filteredList = [];\n var ids = Object.keys(resources).filter(function (id) {\n return id.indexOf('$') === -1;\n });\n ids.forEach(function(id) {\n filteredList.push(resources[id]);\n });\n return filteredList;\n }\n\n\n function isInclude(resource, fields, terms) {\n if(!resource || !fields || !terms) {\n return false;\n }\n return _.any(fields, function(field) {\n return _.any(terms, function(term) {\n return StringUtil.equals(resource[field], term, true);\n });\n });\n }\n\n return {\n filterResources : filterResources,\n cleanResourceList:cleanResourceList,\n cleanResourceMap : cleanResourceMap\n };\n }\n angular\n .module('app')\n .factory('FilterUtil', FilterUtil);\n})();\n","(function() {\n 'use strict';\n\n /**\n * PhraseUtil.js\n *\n * @ngInject\n */\n function PhraseUtil(TransStatusService, _) {\n\n function getSaveButtonStatus(phrase) {\n if (hasNoTranslation(phrase)) {\n return TransStatusService.getStatusInfo('untranslated');\n }\n else if (hasEmptyTranslation(phrase)) {\n return TransStatusService.getStatusInfo('needswork');\n }\n else if (hasTranslationChanged(phrase)) {\n return TransStatusService.getStatusInfo('translated');\n }\n else {\n return phrase.status;\n }\n }\n\n function hasTranslationChanged(phrase) {\n // on Firefox with input method turned on,\n // when hitting tab it seems to turn undefined value into ''\n var allSame = _.every(phrase.translations,\n function(translation, index) {\n return nullToEmpty(translation) ===\n nullToEmpty(phrase.newTranslations[index]);\n });\n return !allSame;\n }\n\n function hasNoTranslation(phrase) {\n return _.isEmpty(_.compact(phrase.newTranslations));\n }\n\n function hasEmptyTranslation(phrase) {\n return _.compact(phrase.newTranslations).length !==\n phrase.newTranslations.length;\n }\n\n function nullToEmpty(value) {\n return value || '';\n }\n\n return {\n getSaveButtonStatus : getSaveButtonStatus,\n hasTranslationChanged : hasTranslationChanged,\n hasNoTranslation : hasNoTranslation,\n hasEmptyTranslation : hasEmptyTranslation\n };\n }\n angular\n .module('app')\n .factory('PhraseUtil', PhraseUtil);\n})();\n","(function() {\n 'use strict';\n\n /**\n * Utility method for handling $resource.statistic\n *\n * StatisticUtil.js\n * @ngInject\n *\n */\n\n function StatisticUtil() {\n return {\n getWordStatistic: function(statistics) {\n return statistics[0].unit === 'WORD' ? statistics[0] : statistics[1];\n },\n getMsgStatistic: function(statistics) {\n return statistics[0].unit === 'MESSAGE' ? statistics[0] : statistics[1];\n }\n };\n }\n angular\n .module('app')\n .factory('StatisticUtil', StatisticUtil);\n})();\n","(function() {\n 'use strict';\n\n /**\n * StringUtil\n *\n * @ngInject\n */\n\n function StringUtil() {\n function startsWith(str, prefix, ignoreCase) {\n if (ignoreCase && str && prefix) {\n str = str.toUpperCase();\n prefix = prefix.toUpperCase();\n }\n return str.lastIndexOf(prefix, 0) === 0;\n }\n\n function endsWith(str, suffix, ignoreCase) {\n if (ignoreCase && str && suffix) {\n str = str.toUpperCase();\n suffix = suffix.toUpperCase();\n }\n return str.indexOf(suffix, str.length - suffix.length) !== -1;\n }\n\n function equals(from, to, ignoreCase) {\n if (ignoreCase && from && to) {\n from = from.toUpperCase();\n to = to.toUpperCase();\n }\n return from === to;\n }\n\n return {\n startsWith : startsWith,\n endsWith : endsWith,\n equals : equals\n };\n }\n angular\n .module('app')\n .factory('StringUtil', StringUtil);\n})();\n","(function() {\n 'use strict';\n\n /**\n * Utility to handles URL related request.\n *\n * UrlService.js\n * @ngInject\n */\n function UrlService($location, $http, $q, $stateParams, _) {\n //IE doesn't support location.origin\n if (!location.origin) {\n location.origin =\n window.location.protocol + '//' + window.location.hostname +\n (window.location.port ? (':' + window.location.port) : '');\n }\n\n var urlService = this,\n gravatarBaseUrl = 'http://www.gravatar.com/avatar',\n configFile = 'config.json',\n baseUrl = '',\n urls = {},\n uiTranslationsURL = location.origin + location.pathname +\n 'translations';\n\n urlService.serverContextPath = '';\n\n urlService.init = function () {\n if (baseUrl) {\n return $q.when(baseUrl);\n }\n else {\n /**\n * Temporary solution to handle dynamic context path deployed for\n * Zanata server in JBOSS (/ or /zanata).\n *\n * If config.baseUrl exist and not empty,\n * baseUrl = config.baseUrl\n *\n * ELSE\n * baseUrl = full.url - appPath onwards\n */\n return $http.get(configFile).then(function (response) {\n var config = response.data;\n if (config.baseUrl) {\n baseUrl = config.baseUrl;\n } else {\n var deployPath = config.appPath.replace(/^\\//g, ''),\n index = location.href.indexOf(deployPath);\n\n urlService.serverContextPath = location.origin + location.pathname;\n if(index >= 0) {\n urlService.serverContextPath = location.href.substring(0, index);\n }\n urlService.serverContextPath = urlService.serverContextPath.\n replace(/\\/?$/, '/');\n baseUrl = urlService.serverContextPath + 'rest';\n }\n\n /* jshint -W101 */\n // URLs over multiple lines are hard to read, allowing long lines here.\n // Warnings for jshint are turned off/on with -/+ before the warning code.\n // See: https://github.com/jshint/jshint/blob/2.1.4/src/shared/messages.js\n urls = _.mapValues({\n project: '/project/:projectSlug',\n docs: '/project/:projectSlug/version/:versionSlug/docs',\n locales: '/project/:projectSlug/version/:versionSlug/locales',\n status: '/project/:projectSlug/version/:versionSlug/doc/:docId/status/:localeId',\n textFlows: '/source+trans/:localeId',\n docStats: '/stats/project/:projectSlug/version/:versionSlug/doc/:docId/locale/:localeId',\n myInfo: '/user',\n userInfo: '/user/:username',\n translation: '/trans/:localeId',\n allLocales: '/locales'\n }, unary(restUrl));\n /* jshint +W101 */\n\n urlService.PROJECT_URL = urls.project;\n urlService.LOCALE_LIST_URL = urls.locales;\n urlService.DOCUMENT_LIST_URL = urls.docs;\n urlService.TRANSLATION_STATUS_URL = urls.status;\n urlService.TEXT_FLOWS_URL = urls.textFlows;\n urlService.DOC_STATISTIC_URL = urls.docStats;\n urlService.MY_INFO_URL = urls.myInfo;\n urlService.USER_INFO_URL = urls.userInfo;\n urlService.TRANSLATION_URL = urls.translation;\n urlService.ALL_LOCALE_URL = urls.allLocales;\n\n urlService.PROJECT_PAGE = function(projectSlug, versionSlug) {\n return urlService.serverContextPath + 'iteration/view/' +\n projectSlug + '/' + versionSlug;\n };\n\n urlService.DASHBOARD_PAGE = urlService.serverContextPath +\n 'dashboard';\n });\n }\n };\n\n /**\n * Get the value of a query string parameter.\n */\n urlService.readValue = function (key) {\n return $location.search()[key];\n };\n\n urlService.gravatarUrl = function (gravatarHash, size) {\n return gravatarBaseUrl + '/' + gravatarHash +\n '?d=mm&r=g&s=' + size;\n };\n\n urlService.uiTranslationURL = function (locale) {\n return uiTranslationsURL + '/' + locale + '.json';\n };\n\n urlService.uiTranslationListURL = uiTranslationsURL + '/locales';\n\n return urlService;\n\n /**\n * Create a REST URL by appending all the given URL part arguments to the\n * base URL.\n *\n * No separators will be added or removed, so all parts should include\n * leading / and exclude trailing / to avoid problems.\n */\n function restUrl() {\n return baseUrl + Array.prototype.join.call(arguments, '');\n }\n\n /**\n * Decorate a function to ignore all but the first argument.\n */\n function unary(fun) {\n return function(arg) {\n return fun(arg);\n };\n }\n }\n\n angular\n .module('app')\n .factory('UrlService', UrlService);\n})();\n"],"sourceRoot":"/source/"}