{"version":3,"file":"bv-lgc-app.515ed4c0a8a1900f.js","mappings":"YAGA,aAIAA,mCAEAC,mHAEA,cACA,mBACA,IAOA,OANAC,cACAC,gBACA,mBACAC,CACA,EAOA,CAJA,MACAC,GACA,uBACAC,CACA,CACA,CACA,CAEA,cACA,mBACA,IACA,oBACA,mBACAF,CAIA,CAHA,MAEA,QACA,CACA,CACA,CAEA,gBACA,mBACA,gDACA,qBACAG,OACAA,8BACAA,CACA,CACA,CAEA,oBACA,oBACA,kCACA,+CACA,EACAC,KACAC,iCAAmDD,cAGnD,OACAE,0BAA2C,CAC3CC,KACAC,aACAC,gDAGAC,wBAA4CC,UAAW,CACvDC,WACAJ,aACAK,0BACAC,WAEAL,8BAEA,OADAb,oBACAG,gBACA,sDACAgB,cACA,EACA,KAGAC,uCAA0DC,aAC1D,CAAkBV,KAASC,aAC3BU,uBAA2DC,aAC3DL,cAGAM,wBAA4CT,kBAAoB,CAChEJ,KACAC,aACAC,oBACA,YAA0C,oBAE1CY,aACAb,aACAK,gBACAS,QACAC,WACAC,YACAC,YAEAX,YAEAY,WACAlB,aACAK,0BACAC,WACAL,oBACA,YAA0C,yBAE1CkB,cACAnB,aACAC,oBACA,YAA0C,gCAE1CmB,aACApB,cACAK,gBACAS,QACAO,uBACAL,cAGAM,YACAtB,cACAc,QAAgCO,iBAChCE,sBAGAC,QACAxB,cACAyB,0DACAX,QACAO,aAEApB,oBACA,YAA0C,oBAE1CyB,KACA1B,aACAyB,0DACAxB,oBACA,YAA0C,oBAE1C0B,QACA3B,iBAEA4B,gBACA5B,cACAc,QACAO,sBAEApB,oBACA,YAA0C,oBAE1C4B,SACA7B,cACAc,QACAO,cAEApB,wCACA,gBAEA,8CACA,IAEA6B,WACA9B,cACAc,QACAO,iBAEApB,oBACA,YAA0C,oBAE1C8B,aACA/B,cACAc,QACAO,mBAEApB,oBACA,YAA0C,oBAE1C+B,iBACAhC,cACAc,QACAO,wBAWAY,YACAjC,cACAc,QACAO,mBAGAa,QACAlC,cACAc,QACAO,gBAIAc,0BAA2C,CAC3CpC,KACAC,cAEAoC,UACApC,cAEAqC,SACAhC,4BACAL,cAEAsC,KACAjC,gCACAL,gBAGAuC,wBAA4CpC,qBAAsB,CAElEqC,cACAxC,cACAc,QACAO,oBAGAoB,YACAzC,aACAc,QACAO,gBACAqB,eAGAC,cACA3C,aACAM,WACAD,4BACAJ,8BAEA,OADAb,oBACAG,gBACA,aACA,EACA,GAEAqD,WACA5C,cACAK,6BACAS,QACAO,mBAGAG,QACAxB,cACAK,6BACAS,QACAO,aAGAwB,QACA7C,iBAEA8C,SACA9C,cACAc,QACAO,eAGA0B,QACA/C,cACAc,QACAO,cAGA2B,SACAhD,cACAc,QACAO,iBAKA4B,qDAAyEC,oBAAqB,CAC9FC,QACAnD,cAEAoD,WACApD,aACAK,0CACAC,cAGA+C,sCAAyD,CACzDC,QACAtD,aACAM,YAEA6C,QACAnD,gBAGAuD,0BAA8CpD,UAAW,CACzDJ,KACAC,aACAC,oBACA,YAAsC,2CAAwCR,KAE9E+D,YACAxD,aACAM,WACAD,2BACAJ,gCACA,cACA,2CACAwD,CACA,IAEAd,cACA3C,aACAM,WACAD,6BACAJ,gCACA,cACA,2CACAwD,CACA,IAEAC,WACA1D,cACAc,QACAO,kBAGAsC,OACA3D,cACAc,QACAO,YAEApB,gCACA,2CAGAwD,CACA,MAGAG,iCAAqDzD,UAAW,CAChEJ,KACAC,cAEA6B,SACA7B,cACAc,QACAO,aACAwC,sBACAC,YAGAC,OACA/D,cACAc,QACAO,aAGA2C,eACAhE,cACAc,QACAO,qBAGA4C,cACAjE,aACAM,WACAD,iCACAS,QAEA,EACAb,8BAEA,OADAb,oBACAG,gBACA,wEACA2E,2BACA,GACAA,CACA,EACA,KAIAC,+CAAmEC,4BAA4B,CAC/FC,SACArE,aACAM,YAEAoB,KACA1B,gBAGAsE,mDAAuEnE,UAAW,CAClFoE,WACAvE,cACAc,QACAO,kBAGAmD,cACAxE,cACAc,QACAO,qBAGAoD,YACAzE,cACAc,QACAO,mBAGAqD,qBACA1E,cACAc,QACAO,8BAKAsD,+CAAiE,CACjE5E,KACAC,aACAM,cAGAsE,8BAA+C,CAC/CP,SACArE,aACAK,8BACAC,YAEAuE,eACA7E,aACAK,2CACAC,YAEAwE,eACA9E,aACAK,kCACAC,YAEAyE,sBACA/E,aACAK,0CACAC,YAEA0E,uBACAhF,aACAK,yDACAC,YAEA2E,wBACAjF,cACAK,sDAEA6E,sBACAlF,cACAK,2CAEA8E,sBACAnF,aACAK,0CACAC,YAEA8E,0BACApF,aACAK,+CACAC,YAEA+E,2BACArF,aACAK,gDACAC,YAEAgF,2BACAtF,cACAK,0DAEAkF,oBACAvF,aACAK,uCACAC,YAEAkF,kBACAxF,aACAK,oCACAC,YAEAmF,iBACAzF,aACAK,oCACAC,cAGAoF,8BAA+C,CAC/CrB,SACArE,aACAK,4BACAC,YAEAqF,YACA3F,aACAK,8BACAC,YAEAsF,WACA5F,aACAK,yCAEAwF,cACA7F,cACAK,wCAEAyF,UACA9F,cACAK,oCAEAqB,KACA1B,cAEA+F,wBACA/F,aACAK,sCACAC,YAEA0F,wBACAhG,cACAK,sCACAC,cAIA2F,8BAA+C,CAC/CC,qBACAlG,aACAK,2CACAC,YAEAuF,cACA7F,cACAK,wCAEAyF,UACA9F,cACAK,oCAEAqB,KACA1B,gBAIAmG,4CAA6DhG,qBAAwB,CACrFiG,UACApG,aACAK,4BACAC,YAEA+F,eACArG,gBACAK,kCAEAiG,gBACAtG,cACAK,iCACAS,QAAuByF,kDAEvBC,oBACAxG,cACAK,iCACAS,QAAuByF,wCAGvBE,qDAAwEtG,UAAW,CACnFuG,SACA1G,aACAK,gCACAC,YAEAqG,WACA3G,cACAK,mCACAS,QAA0BO,kBAE1BuF,YACA5G,cACAK,mCACAS,QAA0BO,kBAE1BwF,WACA7G,cACAK,gCACAS,QAA0BO,yBAG1ByF,4BAA+C3G,UAAW,CAC1D4G,eACA/G,aACAK,2DACAC,YAEA0G,mBACAhH,aACAK,+EACAC,YAEA2G,aACAjH,aACAK,2DACAC,YAEA4G,kBACAlH,cACAK,6CAEA8G,oBACAnH,cACAK,4CAEA+G,wBACApH,aACAK,4CACAC,YAEA+G,aACArH,cACAK,sBACAS,QAA0BO,0GAE1BiG,cACAtH,gBACAK,sBACAS,QAA0ByG,iBAG1BC,gCAAiD,CACjD/G,OACAT,aACAK,uBACAC,YA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A,cACAA,gBACA,EACA,EAAiBA,cACjB,EAEAH,0BACAA,iBACAQ,0CACA,mEAAyGE,iBACzG,gBAKA,+CACAP,gBACA,GANAA,gBAOA,EAAqBA,cACrB,EACA,EAEAH,2BACA,wBAEAO,6DAAyGG,iBACzGF,wCACAL,cACAA,cACAA,gBACA,EACA,EAAiBA,cACjB,EAEAH,6BACA,iDACA,MACA,8BACA,gCAEAjP,wBACAwQ,2BACAT,iBACAX,cACAA,8BAEA,GAGAoB,sBACApB,cACAA,mCAGA,CAEA,kCACA,oCAEApP,wBACAyQ,2BACAT,iBACAZ,gBAEA,GAGAqB,sBACArB,gBAGA,CACA,CAEA,qBACAG,SACA,EACA,EAEA,MAEAgB,aACAG,GACAA,IAGAjB,uCACAD,2DACA,IAEAY,GAFA,UACAO,qBACAC,sGAEA,gCACAxB,6CACAA,4BACAA,6BACAA,uCACAA,2FAGAA,eACAA,yCAGAA,mBACAA,gBACA,EAAqB,YACrBD,eAEA,8DACAC,mBACAsB,8BACAH,GACA,IAGAnB,UAGAA,aACA,IAGAA,mBACAsB,8BACAH,GACA,GAEA,EAEAA,GACA,CACA,IAIAtQ,6DACA,OACA4Q,aACAC,YACAC,iBAIAtQ,iCACA,sBACAuQ,sBAAmCC,wCAC1BC,GACT,yBACAC,kCACAA,iCACAA,gCACAA,gCAEA,wBACAA,iCACAA,gCACAA,gCAEA,uBACAA,gCACAA,gCAEA,uBACAA,gCAGAxC,oBACAA,oBACA,GAKAA,4BACAvO,aACAA,eAGAuO,gCACA,aACAyC,OAEAD,UACA,UACAC,uBAGA,oCACAA,wBACA,cACAA,0BACA,eACAA,mBACA,kBACAA,+BACA,gBACAA,iCACA,qBACAA,uCAEAhR,eACA,GAGAK,6BAEAL,kBADAK,wCACA,2BAEA,2BAGAL,qBADAK,2CACA,8BAEA,gCAGAL,wCACAA,8CAEA,EAEAuO,8BACA,kBACA0C,2BACAA,wBACAA,yBACAjR,WACAA,YACAA,cACAA,YACAA,sBACAA,WACA,EAGAuO,mBACA,sBACA,yBACAvO,iBAA8BkR,uCAC9B3C,gCAEAwC,YACAA,mCACA/Q,4BAIA,4BACAA,sCAEA,uDACA,sBACAA,yBACAA,2BAEA,wBACAA,2BAGA,GAEAuO,mBACAvO,qCAEAA,CACA,EAEAuO,+BAGA,sBACA,qEACAvO,6CACA,GACA,iDACA,sBACA,GACAJ,gCACA,qBACAmR,IACA,uBACA/Q,WAEA+Q,WACA/Q,wBACAA,kBAEA,GACAA,CACA,EAWAuO,0BACA4C,uCACA,sEACAtC,uBAEA,sBAIAA,OAEA,EAEAsC,uCAEA5C,gCACA,YACA6C,OAAiCV,uBACjCA,eACAA,sBACA,yDACA7B,gBAGAA,aADsBwC,kBACtBC,kBAEAD,IAIAxC,qEACA,EAAaA,KACb,EAEAsC,sCACA,sBACAtC,iBAAuC0C,iCAEvC1C,OAEA,EACAsC,kCACA,2DACAtC,SACA,EAAaA,MACbA,IACA,CACA,EAGAN,mCACA,YAAsBmB,WAAgBA,IACtC,8CACA,SAEA,QACA,EAsBA,oBACA,YACA8B,EACAC,GAEAD,oBACAC,oBACA,UAJA,GADA,EADA,CAOA,EAEAC,gBACA,qBACA,EAEA,8BACA,eACA,QACAC,KAEAtR,8BACA,gBACAuR,oBAEA,qBACAA,yBAIA,oBAqBA,MApBA,+CACAC,IACAF,MAEA,eACAA,KAEAZ,EAGA,iCACAc,IAEA,iBACAA,IACAF,MAPAE,IAcAC,OAFAH,EACAzF,EACA,cAAkD,uCAElD,cAAkD,uCAGlDA,EACA,cACA,+CACA,EAEA,cACA,+CACA,EAEA,EAEAqC,CACA,IAMA1O,2EACA,OACA4Q,aACAC,aAwHA,MArHA,+FACAnC,iBACAA,4BAEA,GAEAA,mBAEA,sBACAwD,oBACAD,SACAE,iBAAkCnB,uCAClCoB,YACAC,eACatS,iBAEb,cACAuS,oCAEAA,6BACA,6BACAA,gBAEAA,aACA,EAEA5D,mBAEA4D,qBACAC,8CACAC,WAIAF,CACA,EAEA5D,4BACA4D,aACAA,eAGA5D,gCACA,aACAyC,gBAIAD,UACAC,sBACA,oCACAA,wBACA,cACAA,0BACA,eACAA,mBACA,kBACAA,+BACA,gBACAA,iCACA,qBACAA,uCAEAmB,eACA,GAKAA,kBAFA9R,4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G,gDACAN,qCACAuG,4CACA,GAEAjG,iDACAN,YACAuG,+CACA,GAEAjG,sDACAiG,mCACA,GAEAjG,kDACAiG,mCACA,GAEAjG,+CACAN,UACA,GAEAM,+CACAN,UACAuG,2CACA,GAEAjG,gDACAN,mBACAA,WACAuG,8CAEA,GAEAjG,+CACAiG,gCACA,EACA,GAEAF,YAEArG,iCAGAA,QADA,eACA,IAGAwG,EAGAxG,oBACA,EAEAA,iCACAA,UACAA,oBACA,EAEAA,CACA,IAEAqF,yCA+BA,MA9BA,CACAoB,sBACA,4DACAC,oCACAA,iGACAA,kCACAA,0CAEA,EACAC,8BAGA,QAFAC,KACAC,eACA1F,IAAuBA,WAAcA,KACrC,sBACA2F,2BACArE,qDACAmE,SACAA,OACc,sBAEdA,KADA,SAGAA,YAEA,CACA,QACA,EACAlU,SAIA,GAEA2S,qGAEA,IAuCAvE,EAvCAd,GAEAA,iBAEA3O,4CAIA2O,yDACAA,yDAEAA,qDAGAA,oDACAA,wDACAA,wDACAA,qEACAA,oEACA,GA0BA,OAxBAA,UAEAA,2BACA,8CACAA,iCAGAA,wBAEA,EACAA,2BACAA,mBACAA,0BAEAQ,kEAEA,EAQAR,wBACA,oCALAc,4BACAA,WAIA,gBACA,EAEAd,wBACA,IACA,oCAIA,CAHA,MACA,8CACA,EACA,CACA,EAEAA,yBAGA,OAAuB+G,+BAGvB,yDAEA,EACA/G,2BAIA,yBACAgH,qDACAA,yDACAA,gEAGAA,SAEAC,wBAEA,EAEAjH,gBACAA,8EAGAA,cAEAa,wCACAb,sBACAA,2BACA,GAAa0B,kBACb1B,sBACAA,yDACAQ,eACA,GAEAR,yDAGAA,uBACA,oDACA,EAEAA,sBAEA,kCACA,EACAA,2BAIA,OACA,mCAEA,EAEAA,gCACA,iBACA,SACA,gDAAkDkH,qBAClDlH,6BAA+CmH,KAC/CC,KACA,GACAA,CACA,EAEApH,4BACAQ,kCACAK,yCACAwG,2BACArH,SACA,EACA,EAEAA,CAEA,IAEAqF,kDAEA,6DACA,mCACArF,MACA,GACAsH,GACA,iCACA,0BACA,mBACA,yBAEAC,GACA,gBACA,8CACA,EACA,gBACA,OACA,eACA,+BACA,4BACaC,6BACb,EACA,gBACA,uBACA,6BAEA,GAEA,gBACA,SACA,6BAA8CC,eAAeX,QAC7D9G,eAAuC8G,OAFvC9G,aAGA,CAEA,OACA0H,QACAC,SACAC,yBACA,eACAC,eACAA,aACA,EACAC,yBACA,aACA,kBACA,aACA,CACA,YAA8BC,KAAUA,KACxC,qBACA,iBACA,qBACAC,iBAA+DC,GAC/D,6CACAC,gBAEAF,CAEA,CACA,CACA,EACAG,yBACA,YAA8BJ,IAASA,IACvC,4CACA,qBACA,MAAuE,wBAAvEF,2BAAuEI,EACvE,CAEA,QACA,GAKA,QACA,IAEA5C,yNACA,qKAEA,UACAjT,KACAgW,YACAC,YACAC,sBACAC,sBACAC,uBACAC,iBACAC,iBAGAC,YAEAC,eACAC,6BACAC,kCACAC,wBACAC,wBACAC,kBACAC,2BAQAC,qBACAlD,sBACAzF,8DACAK,sEAEAoF,sBACAzF,6DACAK,mEAEA,EAqjBAuI,EACAC,EAEAC,EAEAlU,EA9ZA,4BACA,OACAmU,OACAC,qCACAC,sDAEAC,QACAF,oCACAG,yCAEAC,OACAC,uCACAC,uCACAC,0CACAP,4HAGA,CA3KAQ,8BAEA,+BAEAA,0BACAb,YACAtI,wBAAqCoJ,kBAAqBjJ,iBAC1D,yCACA5O,KACA4X,UACAA,iBAEAvE,2BACA5E,sDACAA,gDACAoF,0BACAA,wBACA,EAAqB,OAErB+D,UACAA,kBAEAA,+BACA,GAAatI,kBACbsI,UACAA,iBACAA,wBACA,IAEAA,sBACA,EAEAA,6BACA,wBACAA,wBACAA,qBAEAA,UACAA,gBACA,EAMAA,uCAEA,6CACAA,wCAEAA,sCAEA,WACApJ,WAGAoJ,qBAEA,GAAahJ,iBACbgJ,uCACA,EAAa,YAEbnK,yBACAa,kBACAC,8CACAuJ,eAGArK,qBACA4F,oBACA0E,gCACA,EAAqB,KAErB,sCACAH,uCACAA,kCAEA,IAEAA,oCACA,EAEAA,wCACA,sCACAA,sCACAA,mCAEA,2CACAA,2CACAA,uCAEA,EAEAA,+BACA,iBAGA,+CACAI,yBAIAC,6CACAC,Y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AC,+CACA,EAEAC,qBACAD,iDACAE,QACA,EAEAC,sBACA/H,yBACA4H,iDACAD,gCACA,EAEAG,kBAEAvD,aACA,EAIA,kCACA,OAAsByD,oBACtBxa,gBAIA,iBAGA,iDAGAkW,YACAtI,oCAAsDE,SAAS,qEAC/DC,iBACA,yCACA5O,KACA4X,UACAA,mBAGAA,UACAA,kBAGA,wBACAA,wBACAA,qBAEA,iCACAA,iCACAA,8BAIAA,4CACAA,0CACAzD,kCACAmH,mBACA1D,oBACAA,2BACA2D,gBAEA,EAAqB,YACrBpH,kCACAmH,WACA,EACA,EAAiB,cACjB1D,UACAA,iBAEA0D,WACA,EACA,GACAA,SACA,EAEA1D,8BACAnJ,sDACAA,gDACAoF,0BACAA,yBAEAR,yBAEA4H,iDAEAxM,8CACAL,8CACA,GAASmB,sBACTqI,aACAzD,mCACAoH,iBACA,EAEA,EACA3D,sBACAA,oBACAA,+BACA1J,UACAA,iBACAA,oBACAA,iBACAA,iBACAA,2BACAA,wBACAA,yBACAA,iBACAA,6BACA,gCAGA,wBACAA,6CACAA,uCACAiG,uCAEA4G,WACA7M,2BACAsN,IAGAT,CACA,EAEAnD,iCACA,2BACAA,+BACA1J,UACAA,iBACAA,oBACAA,iBACAA,iBACAA,2BACAA,wBACAA,yBACAA,iBACAA,6BACAE,uBACAwJ,2CACAzD,kCACAyD,oBACAA,2BACA2D,gBAEA,EACA,EAEA3D,8BACA,WAGA,+CAA2D1W,gDAC3D,CAAayN,SAAS,mEAAkEkJ,kBACxFjJ,iBACAP,eACAA,aACA,EACA,EAEAuJ,6BACA,WACA6D,oBACA,+CAA6Dva,yBAC7D,CAAayN,SAAS,mEAAkEkJ,kBACxFjJ,iBACAP,8BACA,EACA,EACAuJ,0BACA,IACA8D,eADAxN,KACAyN,OACA,uCACAD,MACA,GAJAxN,KAKA0N,cACA,EAGAhE,yBACA,2BACA,EAEAA,2BACA,WACA,wBAGA,cACAvJ,oBACA,8BACA,+CAA6DnN,yBAC7D,CAAayN,SAAS,mEAAkEkJ,kBACxFjJ,iBACAP,8BACA,EAAiB,CACjB,EAGAuJ,sCACA,WACA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6CAOrC,GALAC,+CAEA/O,aACA+O,qDAEAnJ,OACA,uDACAnS,GACAnC,KACA0d,oCACAC,iBACAC,yBACAC,YACAC,4BACAC,sBACAC,SACAC,kBACAC,mDACAhc,kBACAic,UACAC,kCACA,iCACA,8BACA,OAA6BC,gCAC7B,GACAC,oCACAC,iCACAC,yBACA,yDAEAC,GACAvQ,gDAGA,EACAwQ,kBAAoC,iEACpCxQ,6DACAyQ,2BAA6C,2DAC7CC,uBAAyC,2DACzCC,oBAAsC,2DACtCC,qBAAuC,uCACvCC,wBAA0C,uCAC1CC,6BACA,gHAIA,GAEAvB,qBACAtb,2BACAA,6DAAkF,2BAClFya,aACA,CAGA,mBAEAza,CACA,EAEAyL,wBAEA,QADA8G,WACA3F,aAAmCA,KAAQA,IAC3C,0CAGA,WACA,EAEAnB,0BAEA,kBACA,+BAEA,cAEA,EAEAA,CACA,IAGAqF,uIAIA,QAEArF,GACAqR,0BACA,MAKA,SAJAC,kBACAD,IACA9K,sCAIA,EACAgL,8BACAD,kBACAD,OACA1D,oBACApH,sCACA,EACA+K,sBACA,QACA,EAEAE,6BACAC,wDACA,GAGAlL,6BACAvG,uBACAA,iBACA,GAEA,kBACA,0EACA,iDACA,EAGA,kCAEA,QACA,+BACA0R,0CACAC,sCACA,cACA1W,0BACAA,4BACAA,0BACA0S,4CAEA,CACA,EAEA3N,6BAGA4R,yBACAA,YAEA,wBACA,4BACA,2BACAA,+BAGAA,MAEA,EAEA5R,oCAEA,qBACA,KACA,IACA,mCACA6R,2BACA,YAAoD,oBACpDA,UACAC,cAGA,CAFA,MACA,MACA,MACkBC,GAClBD,eAEA,CACA,EAEA9R,CACA,IAGAqF,sCAEA,SACA2M,GACAC,0CACAC,8CACAC,8CAaA,MAVA,CAEAC,iBACAJ,iBACAK,6BAEAA,WACA,EAIA,GAEAhN,2EACA,SAEAiN,qBACArD,WACAC,eAGAqD,KAIA,yBACA,KAEA,gBACAlD,OAEA,eAEAiD,QADAE,GAAwBjD,eAGxBF,OACAxO,gCACA,YACA,oBACAjP,2BACA4gB,gBAEA,QACA,EAAqB,YACrB,kBACA,IAIAnD,aACA,EAEArP,CACA,IAEAqF,qDAgBA,MAfA,CACA3S,SACA+f,eACAC,kCACAxK,qBACAyK,6BACAC,WACAtQ,WACAuQ,OACAC,aACAC,QAKA/S,iBACAM,gBACAA,YAEAA,gBACAA,YACA,EAEAN,iBACAM,aACAA,kBACA,OACA0S,qCACAL,gBAEA5R,KAEAT,gCACAS,6DACAkS,2BAGA,WAEApS,oBADAP,+BACA,CAAyCvN,qBACzCiO,iBACA3P,wCACAoP,eACA,CACAnM,yBACA4e,uBACAC,iBACAjD,mBACAkD,iCACAC,kBACAC,4BAEA,GAEA7S,yBACAA,wBAA6C8S,yCAC7C9S,sEAEAA,8BACAA,WAEAA,eACAA,SAEA,EAAa,YACbA,UACAA,eACAA,SACA,EACA,EACAT,mBACAM,sBACAA,YACA,EAKA,IAEA+E,uDACA,OACAmO,QACAC,SACAC,cACAC,YACAC,YAGA5T,wBACA,2BACA,EAEAA,qBACA,mCACA,EAEAA,gBAEA,GADAyP,kBACA,yBACA,0CAAgE,OAChEnP,iBACA,CACA,EAEAN,oBACA,YAA2B6T,oBAA2BA,IACtD,8BACA,OAKAvT,yCACAA,kBACA,EAEAN,2BACAM,mBAEA,QADAwT,KACAC,IAA4BA,WAAsBA,KAElD,QADAC,KACAC,IAAgCA,oBAA2BA,IAC3D,6BACAD,KACA,KACA,CAEAA,GACAF,QAA8BrD,WAE9B,CACAqD,aACAxT,gCACA,EACAN,uBAEA,QADAkU,KACAL,IAA2BA,oBAA2BA,IAEtDvT,wBACA4T,MAIA,MACA5T,sBAIA,EAEAN,mBACA,2CACA,OACAM,6BACAA,iBACA,EACAN,0BACA,2BACAM,gCAEA,EACAN,qBACAM,aACAA,iBACAA,mBACAA,aACA,EACAN,yBACAM,aACAA,gBAEA,EACAN,0BACAM,cACAA,kBACA,EACAN,sBACAmU,EACA7T,8BACAA,mBACA,EAAaA,MAEbA,gBAEA,EACAN,mBAEA,6CACA,EACAA,wBACA,QACA,2BACAM,sBAEA,QACA,GA3GA,OA4GAN,CACA,IAEAqF,yFAQA,MAPA,CACA+O,SACAC,SAKArU,gBACA,WACAsU,YACAzT,0CACAyT,iBACA,GACA5S,kBACA4S,WACA,GAEA,gBAEAxT,mCACAA,KACAD,0CAAyEoJ,kBAAmBjJ,iBAC5FuT,iBACA,GACA7S,kBACA6S,WACA,GAGAA,aAEA,GAGA,gBACA,kCACA,kBACA/J,sBACA/J,8DAEA,GAEA8T,SACA,GAASvT,iBACTwJ,sBACAgK,uBACA/T,8DAEA,GAEAgU,kBACA,EAAS,YACTA,WACA,GACAA,SACA,EAGAzU,uBAIA,UAFAM,4BACAA,oBACAlO,6BACAkO,2BACA,WACAkK,KACA,8EACAA,kBACA,GAEAA,4CAAoE,OAAQpY,+BAE5EyO,8CAA4EoJ,kBAC5EjJ,gBACAP,uBACA,EAAyB,YACzBA,wBACAA,mBACA,EACA,CACA,EACAT,yBACA,eACAjN,KACA1B,kBACA0B,yCAA8D,OAAQX,qCACtEW,cACA,WACA,4CAA+DgO,SAAS,mEAAkEkJ,kBAC1IjJ,gBAA8BP,UAE9B,EACAT,wBACA,eAEA0U,IAQA,GAPAlB,sBACAlT,8BAEAA,0BAA4ClO,8CAC5CsiB,IAEA,EAASpU,MACToU,EACA,yBAIA,EACA1U,yBACA,aACA,WACA,kEACA,mDACA,GACAwT,6BACA/S,0BACA,GACAH,oCACAE,mBACA,GAAakB,kBACbjB,QACA,EACA,CACA,EACAT,uBACA,WACA,8BACAa,GAAsB5O,2DACtB+O,uBACAP,UACA,EACA,EAEAT,mBACA,SACA,mDAA0D2U,uBAAsCrU,MAChGqU,qBAA8C,iEAC9C,EAEA3U,sBACA,SACA,mDAA0DM,8CAAsEA,MAEhIsU,qBAA8C,iEAE9C,EACA5U,oBACA,SACA,mDAA0DM,8CAAoEA,MAC9HuU,qBAA6C,iEAE7C,EAGA7U,4BACA,gBAEA,0DADA,CAAsB8U,YAEtB,CAAa/T,SAAS,mEAAkEkJ,kBACxFjJ,iBACA+T,kCAAkE,mBAClEzK,qBACA,GAAiB5I,kBACjB4I,WACA,GACAA,SACA,EAEAtK,4BACA,gBACA,0DAAkEiK,kBAAmBjJ,gBACrFH,4DACAkU,kCAA6D,mBAC7DzK,qBACA,GAAa5I,kBACb4I,WACA,EACA,GAAS5I,kBACT4I,WACA,GACAA,SACA,EAEAtK,4BACA,gBACA,UAAe/N,gEAAuE+O,gBACtFH,4DACAkU,kCAA6D,mBAC7DzK,qBACA,GAAa5I,kBACb4I,WACA,EACA,GAAS5I,kBACT4I,WACA,GACAA,SACA,EAEAtK,gBAEA,oBAEA,oBACAgV,cACAnU,yDACAmU,kCAA+D,kBAC/D,IAGAA,IAEAA,gCAEAA,8BACAC,6BACApF,QACAA,+CACAA,6BACA,EAAiB,cACjBmF,aACA,EACA,IAEAA,CACA,EAEAhV,yBAEA,wBACAM,+BACAA,6BAEAA,aAGA,EAGA,IACA+E,yCAKA,mBACA6P,YACA,WACA,cACA,gCACAC,CACA,CACA7U,iBACAA,uBACA8U,0BACA/jB,gDACAoP,aACA,EACA,EACAH,sBACA,sCACA,YAAiC,sBAA2B+U,kBAC5D,EACA/U,wBACAjP,mBACAikB,sBAAgD7U,qBAAoC4U,YACpF5U,mBACA,EACAH,oBAA+B,kDAAwD,sBAA0B,CACjH,CACA,GAEA+E,4DAIA,MAFA,CAEArF,4BACA,SAEA5N,OAoBA,OAjBAib,QACAnD,WACA5X,gCACAijB,SACA5iB,SACA6iB,+BACAzU,0BACA0U,oBACArjB,0BACA,EACAya,kBACA6I,YACAA,8DAEA,IAGA,aAGArI,QACAnD,YACA5X,oCACAijB,SACA5iB,SACAoO,0BACA0U,oBACAC,WAEA,EACA7I,kBACA6I,YACAA,4CACA,IAGAA,CACA,EAGA,IAEArQ,mEAKA,MAJA,CAAgBsQ,WAIhB3V,gBACAM,gBACA,WACAO,qBACAG,iBACAP,yCAAuE,mCACvEA,YACA,GAAaiB,kBACblB,gBACAC,YACA,EACA,EACAT,sBACA,gCAA8CmB,KAAQA,IACtD,iCACA,kFACAb,6CACAO,kDACA+U,4BACAA,SACAX,4CACAzU,eACAoV,OACA,EACA,EACA,GAEAtV,gBAGA,WACA,EAEA,IAEA+E,oEAEA,OACAoK,WACArF,WACAyL,WACAC,UAEA9V,iBACAA,aACAA,aACAA,aACAA,WACA,EACAA,gBACAM,aACAA,gBACAA,aACA,WACAO,iCACAG,iBACApP,2BACA6O,yBACAK,sCACAL,gBAEA,GACAA,uCAA+D,uCAC/DA,YAEA,EAAa,YACbD,eACAC,YACA,EACA,EACAT,mBACA,8BACA,0DAA2E+V,gBAC3E,CAAiBhV,SAAS,mEAAkEkJ,iBAG5F,GAjCA,OAkCAjK,CACA,IAEAqF,2EACA,OAAgB+E,WAEhBpK,yBACA,gBACA,oBACAgW,yDACAhW,aACAsK,YACA,GACAA,SACA,EAEAtK,yBACA,gBACA,2BACAgW,gCAA4C5jB,SAASb,4CACrDmP,OAAkCC,mDAClC2J,YACA,EAAa,YACb5J,oDACAF,gBACA8J,UACA,GACAA,SACA,EAEAtK,0BACA,gBACA,2BACAgW,iCAA6C5jB,SAASb,4CACtDmP,OAAkCC,mDAClC2J,YACA,EAAa,YACb5J,oDACAF,gBACA8J,UACA,GACAA,SACA,GApCA,OAsCAtK,CACA,IAEAqF,uFAEA,MADA,CAAgB4Q,YAAY7L,WAC5BpK,gBACA,WACA,uBACAa,wCAAsEoJ,kBACtEjJ,iBACAP,kCAA4D,oBAC5DD,wBACAC,YACA,EAAa,YACbD,eACAC,YACA,EACA,EACAT,mBACA,SACA,yBAEAwK,yBACAA,4BACAA,iCACAA,0DACAA,8CAEAA,sBACAA,sBACAA,aACA3J,mCAA0DE,SAAS,mEAAkEkJ,kBACrIjJ,gBAEA,EACA,EACAhB,mBACA,WACA,UACA/N,gBACAK,uBACA2X,gBACAlJ,SAAqB,qEACRC,gBACb,8BAAuDG,KAAQA,IAC/D,kCACAV,wCACA,KACA,CAEAC,OAAkCC,qDAClC,EAAa,YACbD,sCACA,EACA,EACAV,gCAGA,EAEA,IAEAqF,4FACA,OACA6Q,cACAvO,UACAwO,UAGAnW,qBACA,gBAEA,iDAA6DoW,WAAe,CAAIrV,SAAS,mEAAkEkJ,kBAC3JjJ,iBACAN,OAA8BC,uBAC9B2J,YAEA,EAAS,YACT5J,6CACA4J,WACA,GAEAA,SACA,EAEAtK,4BACA,gBAEA,0CACA5N,GAIAA,eACA4N,YACAA,gBACAA,gBAGAA,SACAsK,wBAGAzJ,sCAAqEoJ,kBAAmBjJ,iBACxFhB,gBACApO,2BACAoO,8CACA,GACAsK,uBACA,EAAiB,YACjBA,WACA,IArBAA,UAuBA,EAAS,YACTA,WACA,GAEAA,SACA,GAjDA,OAmDAtK,CACA,IAGAqF,4GAKA,MAJA,CACAgR,UAGArW,gBACA,gBACA,2DACAsK,iBACA,EAAS,YACTA,WACA,GACAA,SACA,EACAtK,qBAEA,mDAAqE1M,kBAA0B,CAAIyN,SAAS,mEAAkEkJ,kBAC9KjJ,iBACAN,OAAkCC,gEAClC,EAAa,YACbD,wCAEA,EACA,EACAV,sBACA,WACAa,GAAe5O,gBACfK,kBACA2X,gBACAlJ,SAAyB,qEACZC,gBACbN,OAAkCC,wDAClCF,WACA,EAAa,YACbC,sCACA,EACA,EACAV,iBACA,gBAEA,+CAIA,kBACA,MANA,EAMA0C,8BACA4H,iCAA6D,wCAA4CgM,IACzG3D,mBAAuD,wDACvD,CACA,EAAS,YACTrI,WACA,GACAA,SACA,EAEAtK,uBACA,WACAM,eACA,gBACA,0CAA0D2J,kBAAmBjJ,iBAC7EpP,2BAMA6O,oBACA,GACA8V,mBACA,GACAA,SACA,EAGA,IAEAlR,6FACA,SACAmR,GAAsBC,8EAEtB,yBACA,gBACA,iDAAiExM,kBAAmBjJ,iBACpF,SACApP,2BACA8kB,2CACAC,oBACA,GACArM,YACA,EAAS,YACTA,WACA,GACAA,SACA,EACAtK,+BACA,UAAsB/N,2DAAiE+O,gBACvFN,OAA8BC,gEAC9B,EAAS,YACTD,sDACA,EACA,EACAV,qBACA,sBACAwK,cACAA,4BACA3J,qCAA6DE,SAAS,mEAAkEkJ,kBACxIjJ,iBACAN,OAA0CC,iEAC1C,EAAqB,YACrBH,eACA,EAEA,EACAR,CACA,IAEAqF,4GACA,SACAmR,GAAsBI,wGAEtB,yBACA,gBACA,kDAAkE3M,kBAAmBjJ,iBACrF,SACApP,2BACAilB,8CACAA,2BACAA,kEAEAZ,iBACA,GACA3L,YACA,EAAS,YACTA,WACA,GACAA,SACA,EAEAtK,qBACA,UAAsB/N,4DAAkE+O,gBACxFN,OAA8BC,sEAC9B,EAAS,YACTD,4DACA,EACA,EACAV,qBACA,qBACAwK,mBACAA,iDAEAA,aACAA,iBAEA3J,qCACA,CAAiBE,SAAS,mEAAkEkJ,kBAC5FjJ,gBACAN,OAAkCC,0DAClC,EAAa,YACbD,+DACAF,eACA,EACA,EACAR,qBACA,yDAA+EiK,kBAAmBjJ,gBAClGN,OAA8BC,wDAC9B,EAAS,YACTD,2DACAF,eACA,EACA,EACAR,CACA,IAEAqF,yEAMA,MALA,CAAgB+E,WAKhBpK,gBACAM,gBACA,WACAgK,YACA,wDACAA,iCAA4D,mBAC5D7J,YACA,EAAS,WACTA,YACA,GACA6J,SAEA,EAGA,IAEAjF,+DACA,SACAyR,IACA9W,GACAoN,aAGApN,0BACA,MAEA+W,EADA,2BACAC,OAEA,iCACAA,aAEA,mBACA,YAGA,KAGAC,IACAF,YAEA/W,OAAiBW,oBACjB,EAGAX,gBACA,WAEA8W,IAKA,iBACA3E,gBACAA,SACAA,eAEA+E,QACA9kB,KACA8X,YACAiN,sBACAxW,yBACAyW,YACAC,8BAEA,sCACA,kBACA,QACA5R,aACAhF,YACA,EAAa6W,EACb,CACA,EACAtX,oBACA,YAAsBmB,WAAiBA,IACvC,eACA+V,cACA,KACA,CAEA,EACAlX,eACA,QACA,EAEAA,uBACAQ,gCACAR,eACA,EACAA,sBACA,mCACA,GArEA,OAsEAA,CACA,IAEAqF,yFAOA,gBAEA,gBAEAoL,EAIA5P,uCACA,YACAyJ,YACA,EAAa,YACbiN,SACAC,OAEAlN,+BAEA,GAZAA,oBAcA,CACA,wGAMA,mBACAhK,kBACAA,aACAA,6BACAA,qBAAmC,aACnCA,eACAA,qBACAA,+BAGA2U,0BAGA,GAFAnjB,gCAEA,CACAwO,mBACA,IACAmX,EADAF,KAaA,GAVAhjB,yCACA,MAxBA,cACA,gBACA,oBACA,CAqBAmjB,kBACAjH,IACAkH,gBACAC,6EACAH,KACAF,UAGA,GACAE,EACAnX,YACAA,oBAA2C,kCAC3CA,0BACkB,CAClB,gBACAkX,OACAK,2BACAvX,YACAA,oBAAmD,mCACnDA,qBACA,EAAyB+U,uBACzB7U,gBACAF,yBACA,EAAyB+U,WAEzB,CAEA,KAAc,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cACAA,sBACA,GACsB,UACtB3L,sCACAG,iBACAwL,cACAA,aACA,GACsB,cACtB3L,oDACAG,iBAEAwL,OADA,mBACA,gCAEAkO,YAEAlO,qBACA,GACsB,WACtBA,wBACAC,qBACA,EACA5L,sCACAG,iBACAwL,cACAA,sBACAA,SACA,GACA3L,yCACAG,iBACA,kCACA2Z,yBACAnO,gBACAA,eACA,kBACAoO,cAEApO,QADAoO,0BACA,EAGA,CAEA,IAEA/Z,sCACAG,iBACAwL,cACAA,aACA,GAGAA,uBACA3L,qCAA0EoJ,iBAC1E,CAA6BlJ,SAAS,qEACtCC,iBACAwL,WACAA,UACAA,iBACA,EAA6B,YAC7BA,WAEAA,eADAnb,oBACA,yBAGA,6BAEA,GACAoU,aACA5E,yCACAG,iBACA,IACA2Z,yBACAnO,cAFAkO,4BAGAlO,cACA,GACA3L,sCACAG,iBACAwL,cACAA,sBACAA,SACA,EACA,EAAyB,KACzB,CAEA,GACAE,kCAEAtB,MACA,EACAyP,uBA6CA,OA5CAzO,QACAE,oDACAC,uEACAC,gBAEA,OACAsO,QAAkC/L,0BAClCgM,QAAkChM,sCAElCvC,sCACA,wCAEA,eAEA,aADAwO,QAA2DC,YAC3DC,GACA,qCACAC,qBACA3O,WACA0I,aACAkG,SACArM,eACAsM,eACAJ,gCAEA,CAEA,2DACAzO,iBACA0I,WACAnG,YACAsM,mCACAJ,mCAEAzO,kBACA0I,WACAnG,aACAsM,oCACAJ,mCAEAE,8BACAA,8BAEA,KAEA/P,MACA,EACAkQ,mBAwGA,OA5FAlP,QACAE,oDACAC,6CA2BA,OACA+O,QACAC,mBACArQ,4CACAD,wBACAE,UAAiDqQ,qDACjD,CAAiDA,qDACjDC,mBAEAC,QACAH,uBACArQ,WACAD,iBACAE,UAAiDqQ,oBACjDC,mBAEAE,MACAJ,6BACAtQ,iCACAE,UAAiDqQ,yCAQjD,aAJA3Y,6BAEA2J,kCACAA,iCACAoP,YACApP,oBAEAoP,cACApP,2BAEAA,eACAA,kCACAqP,mDACAD,WACApP,sBACAA,8BAEA,QADAsP,KACA3a,IAAoDA,sBAA8BA,IAClF2a,4CAEA,QAEA,GAEAF,SACA,qBACApP,iCACkC,gDAClCA,oCACkC,0BAClCA,8BAIA,GACAmN,SAA2BiC,mBAC3B,QACA,GACAlP,iCAGA,EAIA,IAEArH,kIAEA,OACA0W,iBACAC,kBACAC,iBACAC,UACAnH,QACAhH,SACAoO,sBACAC,mBAIA5b,sDACA,iBACAM,uCACAA,gBACA,0CACAd,uDAGA,EACA,EACA,WAEAuG,+BACA/F,yCACAR,oBACAA,wBACAqc,GACA,GAEArc,mBACAA,mBACAA,YACAA,aAEA,EAEAA,kBACAA,WACA,EAGAA,6BAEA,OADAsc,8BAEA,EAEAtc,8BACAA,iCACAA,kCACA,KACAA,WACAA,SAIAA,WACAA,SAGA,EAEAA,sBACA,gBAEA,0EACAA,eACAsK,kBACA,EAAS,WACTA,UACA,GACAA,SACA,EAEAtK,yBACA,gBACAa,6EAAwGoJ,kBAAmBjJ,iBAC3HsJ,iBACA,EAAS,WACTA,UACA,GAEA,kBAAuCiS,uDACvC,2BACA,EAGAvc,qBACA,gBAEA,uCAEAsK,+BACA,EAAS,WACTA,UACA,GACAA,SACA,EAEAtK,oBACA,gBACA,gCACAa,8DACAyJ,YACA,EAAS,WACTA,UACA,GACAA,SACA,EAGAtK,oBACAA,WACAyF,aACAzF,iCACAA,UACA,EAAiB,YACjBA,UACA,EACA,EAAawc,EAEb,EAEAxc,kBACA,gBACA,gEACAsK,iBACA,EAAS,YACTA,WACA,GACAA,SACA,EAEAtK,yBACA,gBACA5N,OACA,QACA,eACAqqB,wCAGAprB,gDACAqrB,SACAD,eACAzc,mBAEA,GAEAa,oFACA,CAAeE,SAAS,2BAA4BC,iBACpDhB,iBAEAA,gBAEApO,2BACA,gBAEA,oFAOA,GACA,2DAsBsB,kCACtB+qB,yBACA3c,oBAxBA,CACAA,yBAEA,SACA6E,oBACAlE,eACAA,cAGAA,cAIAA,MADAkE,QACA,qCAGAA,UAGAnE,SACAmE,yBACAhE,6CACA,KA9BA,CACA,4BACAb,yBAEAuG,iDACA1B,yBACAhE,6CACA,CA2BA,GACAyJ,yBACA,EAAa,YACbA,wBACA,EACA,MAEAA,0BAEA,gBACA,EACAtK,CACA,IAEAqF,wEAEA,OACA0P,QAGA/U,mBACA,gBACA5N,OACA,WACAyO,uDACAb,gBACApO,2BACAoO,kBACA,GACAsK,iBACA,EAAa,YACbA,WACA,GAGAA,oBAEAA,SACA,EAEAtK,mBACA,gBACA,wDACAsK,YACA,EAAS,WACTA,WACAtK,WACA,GACAsK,SACA,EAEAtK,oBACA,gBACA,8CAAsD1M,eAAc,CAAGyN,SAAS,qEAAmEC,gBACnJsJ,YACA,EAAS,WACTA,WACAtK,WACA,GACAsK,SACA,EAEAtK,mBACA,gBACA,8CAAsD1M,cAAa,CAAGyN,SAAS,qEAAmEC,gBAClJsJ,YACA,EAAS,WACTA,WACAtK,WACA,GACAsK,SACA,EAGAtK,mBACA,gBACA,uEACAsK,YACA,EAAS,WACTA,WACAtK,WACA,GACAsK,SACA,EAEAtK,gBACA,gBACA,mDACAsK,iBACA,EAAS,YACTA,WACA,GACAA,SACA,EAEAtK,mBACA6E,MACAhE,+CACAxP,wBACA,EAGA,GAlFA,OAoFA2O,CACA,IAGAqF,gFAEA,OACA0P,QACA6H,cACAC,QACAC,SACAnK,MACAzK,yBACAvK,YAIAqC,mBACA,gBAEA,OADAc,KACA,EACAD,2CAEAb,QACA,kBACA,0CACA,uCACAgB,iBACAhB,iCACAA,gBACA+c,gCACA/c,cACA,GAEAA,wBAA4CuT,yCAC5CjJ,iBACA,EAAa,YACbA,WACA,GAGAA,oBAEAA,SACA,EAEAtK,mBACAA,sBACAA,WACA,EAEAA,uBACA,gBAEA,qDACAqN,SACA/Z,YACA0pB,uBACAzH,UAEA,CAAaxU,SAAS,qEACtBC,gBACAsJ,YACA,EAAS,YACTA,WACA,GACAA,SACA,GAlDA,OAqHAtK,CACA,IAGAqF,yEAEA,OACA0P,QAGA/U,mBACA,gBAEA,OADAc,KACA,EACAD,8CACAb,gBACA+c,2BACA/c,cACA,GACAsK,iBACA,EAAa,YACbA,WACA,GAGAA,oBAEAA,SACA,GAlBA,OAoBAtK,CACA,IAEAqF,kCAGA,mBACA,0DACA/E,mBACAA,iCACAA,oBACAA,uBACA,oCACA,EACAA,yBACA,yDACA,EACAA,yBACA,+CACA,CACA,CACA,GAEA+E,+IACAzE,WAGA,cAMA,GADAqc,UACAA,aAEA,uDAIA,aAFA3c,sBACAA,oBACA4c,GACA,6CACA5c,wCAGA2c,cACAA,cACAC,iBAEA,KAAU,kCACV5c,WACAA,iBACAA,iBACAA,+CAEAA,kBACAA,6CAuCA,aApCAA,qBACAA,uBACAA,oBACAA,qDACAA,aAAwBuM,+BACxBvM,aAGAQ,cACAR,6DACAA,uCACAA,4BAIA+G,4BACA/G,4BAKA,2BACAA,uCACAA,wBAEAA,kCACAA,sEACc,wDACdA,0BAGA,4BACAA,uCACAA,uBAGAA,iBACA+M,4EAGA/M,6BAEA,OAA4B5N,cAC5B,mCACA,0BACAyqB,0BAEA,+BACA,eACAA,kBAIAA,OAHA,mDAGA7c,UAFA,eAIA6c,CAEA,EAEA7c,sBACA,wDAA6E8c,YAC7E,EAEA9c,4BAGA,SACA,yBACAjP,kBAEAgsB,OAGAhsB,gCACA,UAEA,2CACAiP,8CACAgd,uBACA,8DACA,EAAyBjI,WACzB,CACA,EAAaA,YAEb/U,IACA,EAEAA,2BAGA,uCACA,uBACAA,qBAEAA,2DACA,WACA,GACA,uCACAA,qBAGAA,IACA,EACAA,0BACA,oCAEA,mBACAid,oCACAA,oCAGAA,QACA,EACAjd,4BACA,8DACA,EAEAA,mBAEA,SAEA,kDACA2G,4BAEA,SACAuW,UAIAC,kCACAD,gBAEAA,oBAGA,EACA,GACAvW,4CACAuW,eACA,GACA1c,sDACA,yFACA0c,4CAIAA,eACA,EAEA,OACA,8BAGAE,0CACAA,4BACAA,oCACAA,oCACAA,qCAGA,SACA,2CAOA,QALAC,2BACAC,0BACAC,IACAC,KAEAC,IAAmCA,OAAWA,KAC9CA,8BACAA,IACAA,WACAF,SAEAE,iBAGAF,EADkBE,OAClB,GAEA,IAEAD,4BAEAJ,kCACAM,8BACAA,mCACA,CAEA1d,2BAEA,IACA2S,kBAEAgL,uCACAA,MACAhL,8BAGA3S,iDACA2S,+BAEA5hB,oBAGAiP,WACA2S,eACAnS,UACAmS,qBAGAnS,WACAmS,iBAEA3S,qCACA2S,kBAEA3S,yBACA2S,kBACAA,QAEA,SAGAiL,gCACAnd,0BAFAT,6BAIA2S,UACAA,wBAEAnhB,+BACAmhB,gBACAA,qDACAA,6DACAA,4BAEA,4CACA,6BAEA,CAAoBlS,mBACpB,EAEAT,wBACA,oBACA,yDAEA,EAEA,IAmDA6d,EAnDAC,YACA9d,4BACA,gBACA,EAEAA,uBAEAjP,eADA,CAAwBgtB,UACxBxe,GAIAS,oBACAA,yBAEA,WAEA,gCACAA,OACAjP,yCAGAwP,iEACAG,iBACApP,oCACA6O,0BACAA,2DAEAA,iFAEAA,kBACAA,sBACAA,iBAEA7O,gCACAwZ,6BACA6J,eAEA,GACAmJ,oBACA,EAAiB,YACjB5d,gBACAC,kBACAA,iBACAA,sBACA2d,WACA,GACAA,SACA,EACA9d,wBACA,oDACA,EAGAA,qCACA,cACA,kCACA,cAAoC,SAEpC,SACA,6BACA,IACA,sBAEAgd,EADApd,4CACAoe,cACAC,OACAjB,oBAAsD,yBACtDkB,MAGA,CAFA,MAEA,CACA,GACAL,GAEA,EACA7d,kCACAme,eACA3B,UACAD,SAEA,OACA,gBACA,gBACA,eACA,iBACA6B,QAGA,kBACAzL,eACAnS,UACAmS,qBAGAnS,WACAmS,iBAGApS,+CAAwE9N,WACxEiO,iBACA,uBACA,oCACAV,8CACA,EAAqBA,MACrB+d,CACA,EAAiBhJ,uBACjB,sBACAzU,WACA,EAEA,EACAN,mBACAqN,+CAAmEyP,YACnE,EACA9c,6BAEA,0BAAyDsS,mCACzD,mBACA+L,SACAA,MACA,EAEAre,wBACA,mBACAqe,SACAA,MACA,EACAre,yBACA,sCACA,CACA,CACA,oCACA,kBACA,gBACAse,CACA,EAEAC,CACA,IACAvtB,mCACA,qBACAgP,iCACA,OAAyBlO,kCACzB0sB,qCACA/O,4BAEA,GAEAzP,uBACAA,mBACAA,iCAEAA,2BAAqCiT,sEACrCjT,yBACAA,iCACAA,gDACAA,gCACAye,6BACAC,2CACA,GACA,iCACAD,kBAEA,GACAze,qBACAA,6BACAmP,EACAnP,wBAEAA,8BAAiDA,4BAAmC+U,WACpF,EACA/U,+BACAmP,SACAnP,qBAEA0E,0DAAwE1E,sBAA8B+U,WAEtG,EAEA/U,yBACA,uEACA,EACAA,wBACA,iEACA,EACAA,+BACA,uCACA,EACAA,8BACA,wDACA,EACAA,wBAGA,QADAuT,EACA1S,IAA2BA,mBAAsBA,IACjDuB,uCAEA,uDAGA,wBAEA,EACApC,0BAEA,YAA4Ba,mBAAsBA,IAClD,+BAEA,QACA,CAEA,CACA,GAEAkE,uDAEA,MADA,CACArF,sBAEAoM,QACAE,wDACAC,6BACAG,oBACAL,kBACAsN,SACAsF,aAIA,EAGA,IAEA5Z,+DAEA,MADA,CACArF,uBACAQ,eACA4L,QACAE,0DACAC,+BACAG,0CACAL,kBACAsN,SACAuF,iBACA,QACA,IAIA,EAGA,IAEA7Z,2DACA,WACA/E,qBACA,QACA,EACAA,yBACA,MACA6e,WACA7sB,UACA8sB,6CACArF,aACA3O,0BACA2O,OACApM,qBAEAA,gBAEA,EACA,GACA0R,kBACAtF,GACAA,SAEA,GAEA,CACA,IAEA1U,iFAEA,OACA0P,QAGA/U,mBACA,gBAEA,OADAc,KACA,EACAD,6CACAb,gBACA+c,2BACA/c,cACA,GACAsK,iBACA,EAAa,YACbA,WACA,GAGAA,oBAEAA,SACA,GAlBA,OAoBAtK,CACA,IAEAqF,wFACA/E,sBACA,MAGAqL,kBACA2T,8MAKAC,gKAPAC,oBASA,8GAISA,GAETF,UACA3T,YACA2T,YACAC,YAGAD,YACAC,aAGAE,EADA9T,cACA9K,qBAA6DlO,UAC7DqO,iBAEA,OADAse,UAEA,GAGAA,eAGA,gBA8BA,OA5BAlT,QACAsT,WACAC,qBACAC,+BACArT,iEACAC,gBACAA,oBACAA,oBACAA,sBAEAqT,QACAA,UAEArT,oBACAC,UACA,EACAjM,+BACA,GAEA6L,YACAyT,iBACArO,cACAkI,SACAkG,iBAAoC,WAEpClT,cAGAvB,wBACAA,EAIAd,aAHAA,WAKA,GAEAA,SACA,CACA,IAEAjF,6EAEA,8BAIA,IACA0a,GACAC,cACAC,iCACAlU,oCAGAmU,qBACAC,c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BA,oCAErBd,QAA6BnU,2DAC7BmU,QAA6BnU,uEAC7B,CAAqBiV,sBACrB,CAAqBA,2BAIrB3yB,8BAAkD6xB,YAIlDqB,UACAxU,4BACAiT,cACAwB,gBACAtB,SACAC,gBACAC,uBACAqB,QACAnyB,6CACA8kB,iBAEA,+CACAsN,2CAEAjT,iCACAuE,8BACA5jB,QACAuyB,YACAD,cACyBtK,0BACzBtoB,WACA2f,4BAEAA,2BACA,EACA,EACAA,wBAEAA,8BAEA,aADA3f,gBACwCqP,yBAA8BA,KACtE,8BACA4N,SACA0C,8BAEA,CACA,EAEAA,4BAUA,QATAmT,GACAC,sBACAF,mDACAD,mDACAI,eAEAjgB,iBACAge,qBAEA1hB,MAAwCA,yBAA8BA,IAEtEyjB,aADAnT,yBACAA,yBAEA,MACAoR,OACA+B,WACAzX,kCAA0E/a,SAAWwyB,aAErF/B,UACA+B,eACAzX,+CAEAA,GACAA,mBACAsE,uBACA,EAEA,EAGA3f,YAEA2f,gDACAsT,WACAtT,mCACAA,4BACAuE,qBAAyD5jB,OAAWgoB,0BACpE/oB,8BACAogB,gCAA4EuS,kCAC5E,EACA,GACAvS,wBAEA,GAGAA,gDACAsT,UACAtT,uBAEA,GAGAA,gDACAA,8BACAsT,WACA,iBACAtT,yBACAA,8CACAoF,uBACApF,+BAEA,IAGAA,2BAGA,IAIAA,gDACAA,8BACAsT,WACA,iBACAtT,yBACAA,8CACAoF,uBACApF,+BAEA,IAGAA,2BAGA,GAGA,eACAA,oBACAA,mBACAuT,GACA,CACA,EACAC,8BACA,qBACAvkB,OAA0CC,gDAAqBuJ,cAC/D,IAGAkC,QACAE,2FACAqT,qBACAC,+BACAE,iBACAzT,kBACAM,UACAJ,mDACAC,SACAA,cACAnb,yCACAsrB,sBACA,GAEAnS,gBACAgC,8IAEAhC,UACAgC,WAEA,GACAmN,SACAnP,gBAA+C,kBAI/CtK,uBACA,EAGA,EAEA,GAEA,EAAS,YACTkK,YACA1J,iCACA,EACA,EAGA,IAEA2E,yJACA,OACA6f,SACA,CAAahb,iCACb,CAAaA,8BAIblK,6BACA,SACA3O,8BACAwS,WACA,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EA,SAKA,MACA,+BACAqiB,IACA,wBACAA,iCACAriB,oBACA,IAEAsiB,6BACA,CAEAtiB,sBACAA,wBACA,EACAA,sBACAA,mBACA+J,KACArO,+BAEA,EACAsE,kCAEAA,aACAnb,iBACAif,uBACArd,mBACA87B,uBACAC,kBACA97B,cACA+7B,WACS,cACTziB,sDACA,4CACA,gDACA,EACA,GACA,2FA4CA,GA1CAA,yBACAA,UACAuiB,YACAvtB,cACA0tB,MACAj8B,UACAyQ,YACAsrB,WACA5e,YACAC,WACA8e,YACAC,SACAC,QACAC,OAEAC,UACAF,QACAC,OAEAE,UACAH,QACAC,OAEAG,eACAC,aACA/c,2DAAwGA,QACxGgd,YAEA,EACAnjB,gBAEAnb,gFACAy9B,uBACAtiB,mDAEA,GACAsiB,8CACAtiB,sBACAA,oEACUsiB,yCACVtiB,iEAEAsiB,gCACA,uDACAtiB,wCACAA,qCACA,CAEAA,8BACAA,eACAojB,UACAC,WACAC,WAEAx8B,SACAy8B,eACAzvB,mBACAuvB,WACAC,UAEA,EACAE,eACA1vB,wBACA,EACA2vB,cACAC,oBAEA,sDACA,qBACA,GACA1+B,gBACA,yCACA,GACA08B,qBACA,EAEA,EACA1hB,qBAEAA,YACA2jB,QAAsBC,WACtBC,WACAC,SACAC,UAEAC,wBACAC,iBACAhrB,aACA+G,uBACA,EACA,GAGAA,iCACAA,eACA+J,KACArO,aAEAsE,QACAA,gBACAA,qBACAkkB,sBACA,EACAlkB,+DAEA,sBACAR,UACAA,8BACA,OACApZ,mBAAuC,eACvC+9B,uBAA+C,MAC/C,eACA,oCACA,wDACAC,gBACA,CACA,SAEA,GAEA,0CAEA,MADA,+CAEA,EAEApkB,8BAEA,SAEAA,yBAEAqkB,EADArkB,6CACAA,8BAEAvF,8EACAuF,qBACAqkB,wCAEA,GACArkB,2BACAqkB,+BAGA/vB,8DACA0L,8BAIAvF,4DACAuF,cACAqkB,iBAEA,GAGA5pB,qCACAuF,cACAqkB,yBAEA,IAEArkB,8CACAqkB,yDAGArkB,gDACAqkB,gEAEArkB,gDACAqkB,2DAEA,KACArkB,sEACAqkB,kDACArkB,kCAAoE4D,iCAEpE5D,wEACAqkB,qDACArkB,mCAAqE4D,+BAKrE,CAHA,MAAc1e,GAEd,YADAygB,QAEA,CAEA,OACAoE,oCACAjjB,QACAqf,mBACAke,UAEA51B,WAEA0S,2CAEA,CAEA,IAEApB,sLACAC,aAGAA,mBACAskB,OAAqB1+B,4CACrB2+B,WAAyB3+B,wDACzB4+B,MAAoB5+B,yCACpB6+B,aACAh2B,aAKAuR,YACAka,gBACAla,+BACAA,yBACAA,0DACAA,yFACAA,+DACA,GAGA1L,uCACA0L,0EACAA,4EAGAA,eAFA1L,6GAGA0L,uCACA,OACAA,kBACA1L,aACA,0CACAowB,UACApwB,YAA8CqwB,sBAE9C,CACA,EACA,GAEA3kB,iCAGA,GADA4kB,KACA,WACA,MAIAr+B,EADAyZ,yBACAA,uCAGA,OAAsD+J,YACtD8a,cAEA1jB,8BAA4DyP,YAC5D,KAAc,CACd,6BACAkU,2BACA,YAAoD,oBACpD3jB,0BAA6Djb,SAAuB,CAAG0qB,YACvF,CACA,EAEA,OAAwB0T,+CACxB,cACAtkB,kCACA,iDAEA,wCACA,qCAFA+kB,kDAKA/kB,sDAEA,kCACAA,0DAGA,EACA,CACA4kB,IAEA5kB,uBACA1a,gGAEAgP,8EACA0L,wBAIAjG,sCACAiG,uBACA1a,0EACA0a,uBACA,GAEAA,gCAEA1a,eACA0a,+BACAA,sBAGAA,2BACA1L,uCAEA0wB,UACAtnB,YACAe,mDACAwmB,yDACAC,SACAC,WAAiCC,gDAEjC,EACA,CAEA,IAEAC,gCACAvlB,kEACAwlB,UACA72B,YAGAsR,mIACAwlB,SAEA,WACAzxB,oBAGAA,wBACA,kBACA0xB,+CAEAlxB,uCACAkxB,4DACAlgC,4DACAkgC,4CACA,uBACAhmB,QAEAA,CACA,GACAlL,OAAyC2J,WAAWwnB,wBACpDD,+CAAmF,kCAEnFA,iCAEA,uCAAyE,yBACzEA,mCAEA,GACAA,iDAEA3gC,yCAEA,GADA6gC,gBACAA,kDACAA,0BACA,kEACAC,0BACAD,6BACA,EACA,KAAsB,0BACtBA,qBACAA,2BAEAA,uBAEA,GAEA5xB,cAEAjP,4CAKA,MACA,YACA+gC,GAAoClqB,UACpCmqB,eACAD,YACA/gC,8BACA,OAA0C4O,SAC1CqyB,4BACA,kBACA,WACAC,OACAC,uBAEAC,gBACAF,wCACAG,sBACA,MAEAA,iBAEAN,gBACA,GACAJ,0BAEA,CACA,2BACAW,GACAzD,gBAEAkD,GAAoClqB,UACpCmqB,yBACAM,YAMAP,YACA/gC,8BACA,OAA0C4O,SAC1CqyB,wBAEA,gCACA,gCACAI,0BACA,SAA8BC,qBAC9B,2BACAD,0BACA,SAA8BF,6BAE9B,IADAF,0BACAA,YAIA,QAHA5G,OACAkH,UAEAzxB,IAAkDA,MAAQA,KAC1D,2DACA,kBACA,MAEAyxB,aACAC,aACAnH,yDACA,CACAkH,UACA,SACAA,sBACA,gEACAtnB,gBACA,GACA1K,0BAEA,QADAkyB,KACAznB,aACA,gBAEA,GADAynB,0BACAD,YACA,gBACAC,iBACA,CACA,CACAJ,yBACA,EAGA,KACA,CACA,gEACAA,iBACAP,0BACAO,gCACA,EACA,MAGAA,wCAEAN,gBACA,GACAJ,qBACA,CACA,GACA/2B,kBACA+2B,mBACA,CAA0B9pB,aAC1BoV,SAAsCrd,sCAItC,EAGAK,2BAGA,sBACA,iBACA5N,KAEAA,eAEAZ,qFACAkZ,2BACA+nB,wBAGAplB,4CAAwEyP,YAExE,EAEA9c,yBAEA,oCACA0yB,cAEA,6BASArU,QAPA,CACApI,IACAjjB,eACAqf,mBACAke,8BAKAljB,2CAEA,EAEArN,wBACA,yBACAqe,SACAA,MACA,CAEA,KAIApS,kOACA,sCAEAC,6BAGAymB,kBAGAzmB,6CACAA,gBACAA,iBAEA,EAEAA,0CACA,YACAA,kCACAlK,+BACA,GAIA,iBACA,iDACA,EAEA4wB,GACA,CAAgBnkB,YAChBwM,uBACAxF,8BACAod,eAAiC,sBACjCC,cACAC,wBAA+DC,UAC/D,GAEA,CAAgBvkB,YAChBwM,uBACAxF,8BACAod,eAAiC,yBACjCC,cACA5B,gBACA,GAEA,CAAgBziB,eAChBwM,uBACAxF,2CACAod,eACA,mBACAryB,OAAyCyyB,eAAgBC,qBACzD,EACAJ,cACA,UACAK,oBAAmE,kBACnE7yB,0BACA,SACAvP,8BACAmP,mCACAnP,oDACAA,gCACAqiC,aACA,EACA,EACA,GACAC,oBACA,EACA,GAEA,CAAgB5kB,YAChBwM,iCACAxF,uCACAod,eAAiC,wBACjCC,cACA5mB,mBACAglB,gBAEA,IAIAhlB,6BAEAA,+BACAA,mBACA6mB,wBAA2DC,UAE3D,EAEA9mB,gCACAA,mBACAglB,gBAEA,EAEAhlB,+BACAA,mBACAglB,gBAEA,EAEAhlB,qBAaAA,2BACA,kBAAkClK,QAElC,OADA0C,wDACA,CACA,EAEAwH,wBACA2H,EACA3H,+BAEAA,gCAEA,EAEAA,oDACAgH,IA1BAhH,2BACAA,mBACA0mB,sBACA9X,kCACA,GAwBA,GAAS,GAGT5O,0BACA,gBACAuX,KACA1yB,oDACA,uBACAA,qCACA0yB,eACA,EAEA,GACAA,SACAzZ,aAEAkC,qDACAhM,eAEAyU,MADAzI,6BACAxL,iBACA,6BACAuC,UACAwgB,oBACAzZ,cAGAA,UAEA,IAGAA,WAGAA,0BACAqpB,gBAA+C5P,yBAC/C,EAAa,WACbrjB,OAAkCC,+BAClC,EACA,EAEA6L,wBAEA1L,kDAQAsL,QACAC,kBACAC,iEACAqN,SACAia,uBACA,4CACA,EACA34B,kBACA,eACA,GAEAsR,4DACAC,WACAA,SACAA,WACAA,gBACAA,mBACA1L,8GACA,MACA0L,WACAA,aAEA,MACAA,WACAA,iBAGAA,UAEA,EACA,CACA,KACiBpB,wBACjB,MACA1K,OACAC,qBAGA,EACA,EACA,EAEA6L,6BACAmnB,8BACA,EAEAnnB,0BACAA,uBAEA1L,kDACAiqB,EAEA8I,MACArC,UACAvmB,+BACAC,oDACAgY,SACAnU,iBACAyM,wBACA/Y,IATAsoB,EASA8I,MACA3pB,cACiC,CACjC6E,eACAyM,gBACA/Y,4BAEA0I,UACA1I,OACA+Y,aACAsY,uBACAzlB,WAC6B,CAC7B5L,OACA+Y,mBAEyBpQ,wBACzB,MACA,OACA9X,WACAygC,sBACAC,mBAGAC,oDACA,CACA,GAEAzC,UACAtnB,cACAuR,qBACAxQ,mCACAC,kFAGA,EACA,EACAsB,4BACA,OACAlZ,yBAEA2gC,oDACA,EAEAznB,0BACA0nB,sBACA,CAEA,IAEA3nB,qGAEAC,YAEAA,oBACA1L,uCACA0L,+CACA,GAEAA,uCACAoF,GACAA,iCAEA,6BACAuiB,KACAC,kBAEA,MAEAD,yBACA,oBACAvP,CACA,EAAyB,IAEzB,4BAA+DzjB,KAAQA,KACvE,kBAEAgzB,YAKAE,kCACAF,YAGA3nB,oBAEA,CACA,CAEA8nB,wBACAF,iBACAtiC,+BACAyiC,gBACA/nB,iBAEA,EAEA,EAGA,GAEAA,2BACA,eACAH,YACAC,oDACAC,0BACAG,iCACAiN,SACA4a,sBACA,QACA,MAGA,CACA,EAEA/nB,kCACAhF,sCACA,CAEA,IAEA+E,gPACAmkB,UACA8D,SAEAhoB,0CACA,4BACA1L,YAAsC2zB,uBAA2BzzB,gBACjE0zB,GACA,EAEA,GAEAloB,sBAEAA,6BAEA,aADAA,iDACAA,mBACAA,sEAEA,EACAA,mEACAgH,IACAhH,mCAEA,GAAS,GAET,aAAyCmoB,eACzCrjB,oBAEAsjB,KACAjW,YACAnS,qCAEAA,WACAooB,MAGA,kBA8CA,GA7CAp0B,gCAEAvF,gBACAsL,4BAGAmqB,mBAEAlkB,2BAEAvR,2BAGA,GAFAsL,+BAAyDjE,iBACzDkK,mCACA1a,6DACA,gBACAgP,uCACAA,WACA,kBAEAmU,kCACA,sBACAxjB,2BAAmF,uCACnFA,EACAkc,+BAA6EknB,uCAA8C,CAAGC,YAE9HnnB,0BAAwE0L,mBAAqB,CAAGyb,WAChG,GAEAnnB,0BAAgE0L,mBAAqB,CAAGyb,YAGxFnnB,aAA+C8B,YAAc,CAAGqlB,WAEhE,EACA,CACArvB,aACAsvB,GACA,EAAiB,GACjB,EAAa,WACbxuB,4BACA,GAKAiG,mDAGA,mBACAwoB,GACAC,gDACAC,kBACA,aAGAF,yCACA1D,UAEAzwB,yBAAmD9N,oBAAmCiO,iBACtFwL,sEACAA,yEACA5a,oCACA4a,0DACAvR,iCACA,oDACA1G,aACA,GAEAA,4BACA4gC,0BACA,GACA,sBACA5gC,yDACA,GACA6gC,gEACA,EAEA,CACA5oB,gEACA6oB,sBACAA,0BAEA9uB,4CACAiG,iCACAA,wDACA,GAGA,EAEAooB,OAEApoB,mBACAA,2BACA,6CACAA,iBACA,EACA,CAEA,IACAD,mFAIAC,WACAA,SACAyI,gCACAzI,OACA,EAIA,IAEAD,uWACAvB,cACAlZ,cACAwjC,SAqKA,GApKA9oB,SAEAA,2BAEAA,kBACAA,cACAA,cAEAA,wBACAA,4BACA,EACAA,iBACAA,2BACA,YAAoC+oB,mBAA+BA,IACnEhhC,aACAihC,WAEA,EAEAhpB,0BACAA,cACA,EAEAA,sBACAA,2BACA,EACAA,+BAEAA,0BAIA,GAHAA,qBACAA,iBACAA,iBACAjY,gCAGA,QADAkhC,KACAC,IAAgCA,mBAA4BA,KAC5D,yBACAC,oBACAC,EACAH,UACAE,GACAF,SACA,CAQA,QAFAI,KACAC,6BACAP,KAAoCA,oBAA+BA,MACnE,qBACAQ,YACAD,oBACAE,YACAH,eACA,CACArpB,qBAmCAA,iBAEA,iBAEAA,yBAEA,IAqBAypB,eACA,OAtBA,YACA,mBACA,gBAEA,6CAAmFtjC,WAAYqO,KA5C/F,kBACA,oBACA,mBACAk1B,YAEA,4CAOA,GANAA,kEACA7hC,kBACA6hC,mBACAA,6CACAA,aAEAA,WACAA,qBACAA,2BACAA,4DACsB,CACtB,6BACAA,sCACA,CACAA,WACAlgB,gBAA8C5jB,WAAsBR,KACpE4O,gBACA5O,aACAskC,wBAKAA,+BACA1pB,iBACAmN,MACA,CACA,CAW+Fwc,OAE/F,WACA5hC,uBAEA,4FAEAqhC,IACAppB,+DAGAmN,MACA,EACA,EAEA,CAGAyc,sBAGA,GADA5pB,cACA6pB,wDACA,0BACAJ,QAGAzpB,oBAIA,EACA,EAEA,6BAEA,yEACA,4BACA,IACA8pB,2CAEA9pB,mBADA,SAFAA,iBAGA,yCAEA8pB,GACA,CAGA7wB,aACA+G,UACA,EAAiB,GACjB,EAtHA,CA6HA,EAEAA,QAEAA,kBACAA,+BACA1a,qEACA,iCACAykC,uBACA,CACA,wDACAlpB,QACAnD,WACA5X,wCACAK,SACA6jC,eACAhhB,wBACAihB,gBACA11B,0BACA0U,oBAEAlhB,YACA,UACAzC,eACA0a,iBACA1a,qEACAykC,qBACAj0B,WAGA,EACAuK,kBACArM,eACA,QAEa,CACb,UACA1O,eACA0a,iBACA1a,qEACAykC,qBACAj0B,WAGA,CAEA,sBACA/N,YACA,QACAiW,oCAA4E,OAASiG,UACrFjE,iBACAA,wBAGA1a,qEACAkZ,2BACA0rB,kCAEA,EACAlqB,2BAEA,QADAmqB,KACApB,IAAkCA,mBAA8BA,KAChE,mBACA,aACA,YACAoB,SACsB,CACtB,eACA5gB,IACA6B,wCAEA7B,EADA,sBACA6gB,+BAEAtD,WACAvd,OACA8gB,iBACApmB,YACAqmB,sBACAC,kBACAC,aAEA,CAEA,CACAL,GACAxkB,uFAEA,EAEA3F,6BACA,YAAkC+oB,mBAA+BA,KACjE,mBAEAS,aADA7hB,gBAKA,CACA,EAEArT,uCACA0L,iBACA,GACAA,+BAAmDA,oBACnDA,gCAAoDA,oBACpDA,2BACAA,mCAEA1a,gBACA0a,6BACA1L,OAAqCyyB,eAAgB0D,gBAAiB,sCACtEn2B,OAAuCyyB,eAAiB0D,iBACxDzqB,6BACA1L,OAAqCyyB,eAAgB0D,gBAAiB,sCACtEn2B,OAAuCyyB,eAAiB0D,iBAExD,EACAzqB,wBAEA,MACA,qDACA,4BAEA,YADA2F,2EAEA,IAEArR,OAAyCyyB,eAAiB0D,gBAAoB,gBAE9E,YADA9kB,sFAIA,CAeA,OAZA/F,QACAC,kBACAC,4DACAC,iCACAG,oBACAiN,SACAplB,eACA,QACA,KAIA6W,uBAEAvK,0CAEAwM,QACAnD,WACA5X,wCACAK,SACA6jC,eACAhhB,wBACAihB,gBACA11B,0BACA0U,oBACAyhB,aACA,EACArqB,kBACA,GAEA,EACA,IACA,CACA,EAEAL,wBAEA,MACA,qDACA,qCAA6E+mB,eAAiB0D,gBAAoB,gBAElH,YADA9kB,qFAEA,GAAiB,iBAA0CohB,eAAiB0D,iBAE5E,YADA9kB,mDAIA,CAEA,QADAglB,KACA5B,IAAkCA,mBAA8BA,KAChE,mBACAS,WACAmB,SAEA,CAEA,eAqBA,OAfA/qB,QACAC,kBACAC,4DACAC,oCACAG,oBACAiN,SACAplB,eACA,QACA,EACA6iC,qBACA,QACA,KAIAhsB,uBAEAvK,0CAGAwM,QACAnD,WACA5X,wCACAK,SACA6jC,eACAhhB,wBACAihB,gBACA11B,0BACA0U,oBACAyhB,aACA,EACArqB,kBACA,GAGA,EAEA,IACA,EA3CAsF,uCA6CA,EAEA3F,aACAA,4BAEAA,kCACA,aACArH,SACAkyB,KAEA,qDACA50B,cACA0C,4CAGAmyB,IACAA,KAGAA,QACAD,KAEA,GAEAA,CACA,EAEA7qB,+BAEA,MACA+qB,KAEAlmC,uDACAoR,IACA80B,UAGAC,EAFAhrB,qCAKA,GAEAmnB,wBACAnnB,2BACA,EAEAA,aAEA,iBACA,OAAmClZ,+CACnCxB,wBACA+O,mCAAsEE,SAAS,qEAC/EC,iBACApP,2BACAijC,iBACAroB,uCACAA,yBACA,EACA,GAEA1a,iCACA2lC,iBACA52B,mCAAsEE,SAAS,qEAC/EC,iBACApP,2BACAijC,iBACAroB,sCACAA,wBACA,EACA,GAEA,EACA1L,kCACAA,WACA42B,IAEAlrB,6BAAqDkrB,KAErD,GAEAlrB,iCACA,oBACA,EAEAA,6BAEA,QADAmrB,OACAC,IAAiCA,mBAA6BA,IAC9DrjC,uBACAojC,gBAIA,OADA3sB,2BACA6sB,gBAEA,CACA,EAGAnpB,6CADAna,QAGAiY,YAEA,IAuKAsrB,EAvKA7nB,mDAOA,GALAzD,mBACAurB,QACAC,yBAA6C,yDAG7C/nB,SAEA,OAAmCwQ,qDAEnCpvB,gBACA4mC,gBACA,cACA9qB,mBACAX,uBACA0rB,2CACA,EACA,EACA,CAEA1rB,oKACAA,4BACAjY,QACA4jC,iCACA5jC,QACA,IAIAsM,6CAAmEua,gBACnE,CAAiBra,SAAS,qEAE1ByL,wBACAA,wBAIAA,cACA,EAGAA,2BACA3L,2DACA2L,kBACA,GAIAA,+BACAA,YACA4rB,iCACAC,sBACAC,mBACA7yB,aACA+G,gBACA,EAAyB,EAEzB,EACA,EACA,IAGAjY,2CACAyhB,wBAAwCvG,YAAc,YACtD8oB,uBAA6D,gCAAuC,KACpG/rB,gBACA,GAGAA,uBACAmB,0BAA4C0L,uBAC5C,EAEA7M,yBAEA,iCACA,CACA,EAEAA,8BACA,yBAEAJ,QACAC,YACAC,uDACAC,6BACAG,iCACAiN,SACAzP,gBACA,QACA,EACA3V,eACA,OACAnC,YACA2d,kBAEA,MAGA,CACA,EAEAvD,2BACA,wDACAJ,QACAC,YACAC,qDACAC,2BACAG,+BACAiN,SACAplB,eACA,OACAnC,YACA2d,kBAEA,IAKA,IAEA,CACA,EAEAvD,uBAEA,cACAglB,UACAvmB,6BACAC,gKACAC,UACAqQ,iBACA/Y,gBACqB,CACrB+Y,sBACA/Y,eAIA2I,wBACA,WACAvK,iDACAG,iBACA,IACAw3B,iDADA,sGACAC,8BACAC,oCACAA,eACAF,YACAnrB,2BACAmrB,WACAA,UACA,GAEArT,4DAEA,GAEAA,4DAGA,EAGA3Y,mBAGAA,0BACAsrB,iDACA/S,UACAyP,sCACAhoB,6CACA,EAAqB,WAErBsrB,IACAtrB,iBACA,EAEA,EACA,EAEAA,2BACA,6CACAA,kBACAA,gBACA,EACA,EAEAA,kBACAA,+BACAsrB,IACAA,IACAA,QAGAtrB,iBACA,GACAA,gCACAsrB,IACAA,IACAA,QAGAtrB,iBACA,GAEAA,kBACAA,eACAA,0BACA3L,4DACA2L,uBACAkO,kBACAlO,kBACAA,2BAEA,EACA,EACAA,iBACAA,2BACA,eACAH,YACAC,yDACAC,8BACAG,+BACAC,UACAgN,SACAgf,gBACA,sBACA,EACAp1B,iBACA,sBACA,MAGA,CACA,EAEA+xB,cAEA9oB,iBAEA,IAEAD,6GACAC,kBACAnb,wCACA,gBACA,+BACAimB,0CAEAA,gDACA/T,MACA+T,8BACAA,qCACAA,kDAGAA,+CACAA,mDACAA,uDAEA,GACA9K,uBACAA,SAEAA,mBACAC,SACA,CACA,IAGAF,2LACAC,WACA6M,kBACAuf,eACArkC,MACAskC,WAGArsB,mDACAA,2CAEAA,WACAA,WAEAA,gBACAyI,kCACA6jB,6BACA,mCACA,kCACAtsB,eAEA,EACA,GACA1L,wCACA0L,oCACA,IAKAA,kBACAA,6BACAusB,mBACAA,oBACAvsB,iBACA,EAEAA,uBACAwsB,oCACAt4B,OACAC,sCACAuJ,iBAEAuC,UACA,2CACAkB,UAEA,GAAajM,kBACbhB,OACAC,qCACAuJ,cAEA,EAEA,CACA,IAEAqC,mHACAC,mBACAA,aAGAA,QACAA,0BACAysB,mCACAv4B,OACAC,0FACAuJ,iBAEAuC,SACA,GAAa/K,kBACbhB,OACAC,qCACAuJ,cAEA,EACA,CACA,IAMAqC,oEAEAC,cACAA,cAEAA,mCACAA,4BACAA,cACAysB,gBAEA,EAEA,IAEA1sB,wKACAza,SAKA0a,+CAgBAA,eACA3M,oCACAq5B,iBACA9U,mBACAC,mBACAF,WACAgV,sBACAC,oBACAC,eACAC,kCACAC,2BACA/4B,cACA,IASAgM,0DAAmF3M,YAEnF2M,gDAEAA,oBAEA,oBACAgtB,KACA,mBACAA,OAEA,oBACAxmB,sBACAwmB,MACA,GAIAhtB,gDACA,+BACAitB,YAEA,KAIAC,EAJAC,6BAEAA,oBACAA,sCAEAF,uBAEAE,kBACA,CAAsBne,MAFtBke,wBAEsB1mB,QAItB2mB,mBACAxV,WACAC,gBACAC,mBACA8U,2BAEAC,oBACAC,eACAC,kCAEAM,oBACAC,eASArtB,yBACA,GAEAA,8BAEAnb,2CACA,MACAyoC,yCAAuE,0BAEvEA,wBACA,2BACAA,oBAEA9mB,MACA8mB,8BACAN,UACAM,aAEA,EAIA,EACA,EACAttB,qDAEAA,SACAtE,SACAqO,KACArJ,iBACA5M,qBACAkM,mBACA,EACAutB,wBACAz5B,qBACAkM,wCACAA,mBACA,GAGAA,2BAEA,OACA+J,wBAAuDrO,oCAEvD8K,cAAoCA,OACpC,eACA,4BACA,CACA,EAEAxG,6BACA,OACA+J,wBAAuDrO,oCAGvD8xB,KACA3oC,iCACA,cACAyZ,UACAzZ,4BACA2oC,uBACc,yBACdA,gBAEAlvB,+BAGA,GACAkvB,qBAA+ChnB,OAC/C,eACA,4BACA,CACA,EACAxG,yBAEA,GADAmB,kBACA7b,qEACA,iCACAykC,4BACA,CACA/pB,eACA,EAEAA,6BACAA,eACA,GACAA,sCACAA,eACA,GAEAA,0CAEA,gCAIA/G,aACA,wBAGA,MACA,+BACA,2BACA,4BACA8Q,qBACA/J,mBAAsD3M,kDAA4D,oBAAuBkF,SACzIyH,iBAEA,2BACA+J,4BAEA/J,WACA,CACA,CACA,QAAiB,GAEjBA,uBACA1a,oBACA,iCACAykC,4BACA,CAEA,EAEA,IAEAhqB,yQACAmkB,mBAKAlkB,SACAjG,aAEAiG,YACAytB,eACAC,WAGApoC,0BACA0a,2BAEAA,2BACAa,qEACAA,8BACA,GAAa8sB,OACb,EACA3tB,gBACA4tB,YAGA5tB,8BAA8CA,gDAC9CA,4BACAA,+CACA,EAEAA,SACA1L,wCACA0L,eACA,GAEA6tB,UAEA9zB,6BACAiG,iBACA1L,wCACA0L,eAEA,GAKArC,aAGA,GAEA5D,4BACAzF,wCACA0L,gBACA1L,YACA,eACAqJ,8BAGA,EACA,GAEAqC,WACAA,SACAA,UACAA,eAKAA,8BAEAA,8BACA,wBACA6E,GACA1D,4CAA+EyP,YAE/E,EAEA5Q,yBAEA,sDAYA,EAEAA,0BACA,0DACAA,wBACAglB,kBACA,IACA,CACA,EAEAhlB,yBACA,wCACAmB,cACA,CACA,EAMAnB,mCACA,eACAH,YACAC,6DACAC,kCACAG,oBACAiN,SACAC,kBACA,QACA,MAGA,CACA,EAEApN,2CACAA,yBACA,GACAA,oCACA1L,gCAAsDw5B,oBACtD,GAEA9tB,8BACA/G,aACAc,YACA,EACA,GACAiG,kCAEA/G,aACAc,YACA,EACA,GAeAA,oBACAA,2BAEA/F,0FAoBA,EAEA+F,uBACAA,qCAEAiG,yBAEAA,4BACAA,+BAEAA,mCAEAA,+BACA,eACAH,YACAC,yDACAC,yGACAC,cACA+tB,eACAC,YACAC,WACAC,YACAC,aACAC,cAGApuB,SACAA,qBACA,SACAnb,8CACAoR,GACAo4B,SAEA,GACArwB,4BACAswB,4BACAruB,UACAlG,mCACA,EACA,CACA,GACAmG,2CAEA,CACA,EAGAnG,sCACA,wEACA,iCAEAgwB,eACAA,oBACAA,0BACAA,WACAA,iBAEAA,yBACAwE,SACAC,eACAC,iBAEA,CACAz6B,+BACA,wBACA06B,IACAA,4BACAvtB,4CAA2EyP,cAE3E5Q,UACA,GAgGAjG,oBACAA,8BAEA,GADAA,mBACA40B,eAKA,CACAt6B,wDACA0F,aACA,SACA40B,sBACA,2BACAC,SAEA,GACAA,YACA/zB,wBAEA5B,aACAc,mBACA,EAAiB,MACjB,aAnBA1F,4CACA0F,aACAc,sBAkBAd,0BACA,EAEAA,uCACA/F,mCACAA,uBACA+F,qCACA,EACA,IAEAgG,oFACAC,gBACAA,WACAA,gBAEAA,uBAEA,eACAH,YACAC,uDACAC,iCACAC,SACAA,0BACA,GACAE,mCAGA,CAEA,EAEAF,yBACAA,sCACAA,gBACA,EAEAA,wBAcA,MAbA,CACA,iCACA,qEACA,6CACA,2CACA,4CACA,oFACA,iDACA,8CACA,2CACA,gDACA,yCAEAwG,KACA,CAEA,IAEAzG,mKACA,gCACAC,WACAA,SACAA,SACAA,eAGAA,UAEA,SACA1L,4BACA,YAEA0L,cADA6uB,KACAxH,KACA,GAEArnB,gCACA1L,kDACA,qBACA,UACAuM,qCACA8G,eACA,GACAqd,eACA,EACA,EACAhlB,+BACA,sDACA,yBACA,GACAgH,UACAge,cAEA,EACAhlB,wBACAA,mBACA2Y,gBACA,EACA3Y,mBAAmC2Y,kBACnC3Y,4BACAA,uBACA2Y,gBACA,EAEA3Y,uBACA8mB,gBAEA+H,QACA7J,UACAvmB,gCACAC,oDACAgY,SACAnU,iBACAyM,wBACA/Y,YACAyH,cAC6B,CAC7B6E,eACAyM,gBACA/Y,6CAEA0I,UACA1I,OACA+Y,aACAsY,wBACyB,CACzBrxB,OACA+Y,mBAEqBpQ,wBACrBkwB,GAMArH,gBALA,CACA3gC,WACAygC,sBACAC,mBAIA,GAEAxC,UACAtnB,cACAuR,qBACAxQ,mCACAC,mFAGA,EACAsB,yBAKAynB,gBAJA,CACA3gC,wBACA0gC,gDAGA,EAEAxnB,mCAA4D+uB,4BAC5D/uB,2BACA,OACAlZ,8BACAkoC,0BAEA,8CAAoEz6B,SAAS,qEAC7EC,iBACAN,OAAsCC,yCACtC,EAAiB,YAEjBD,OAAsCC,IADtC,yCACsCuJ,yBACtC,EAEA,EAEAsC,yBACAA,oBACA,EAEAA,sBACAA,4BAEAA,oCASA,GARAA,OAA0BivB,eAC1BjvB,iCAEAA,wBACAA,yBACAA,sBACAA,4BAEA,uBAGA,6BAEAkvB,8DACAlvB,gDACAA,iCACA,EAEAA,+BAGA,QADAmvB,KACAC,IAAmCA,sBAAqCA,IAExEA,MACAD,iBAEAA,GAJAnvB,gBAIAiE,KAGA,QACA,EAEAjE,2BAGA,QADAmvB,KACAC,IAAmCA,sBAAqCA,IAExEA,MACAD,QAEAA,GAJAnvB,gBAIAiE,KAGA,QACA,EAEAjE,kCAEA,QADAqvB,KACAD,IAAmCA,sBAAqCA,KACxE,sBACAC,mBACA,CACA,QACA,EAEArvB,kBAIAA,yBACAA,0BACA,EAEAA,oBAGA,GAFAA,kBAEA,kBAIA,kEAKA,SAEAsvB,sCACAA,gCAAmE,gBAAe5N,UAClF4N,wCACAA,iDACAA,uBACAA,2BAEAzuB,wBAAsCnC,gFAEtCmC,QACAnD,YACA5X,2BACAkY,OACAzJ,0BACA0U,oBAEA,8CAEAjJ,qBAAuDtC,6FAEvD,uBAEAxJ,OACAC,oJACAuJ,iBAEAsC,wBACAA,YAGAhC,sBAEAgC,qBAAuDtC,0GAEvDmD,0BAEA,EACAR,kBACAQ,2BACAb,qBAAmDtC,mCACnD,GA5CA,MAFAsC,qBAA2CtC,uDAJ3CiI,+BAoDA,EAEA3F,4BACA,sBAIA,IAEA4D,sEAA6G5D,wBAC7GuvB,qBACAC,OACAC,OAEA,OADAzvB,qBAEA,SAGAwvB,EAFA,SAEA,CAAqCE,sBACrCC,cACAC,gBACAC,gBACAC,2BACAC,eACAC,gCACAC,qBACAC,SACAC,eAnBA,MAoBAC,eArBA,MAwBA,CAAqCV,sBACrCC,cACAC,gBACAC,gBACAC,2BACAC,eACAC,gCACAE,eACAD,cACAG,eAjCA,MAoCAX,KAEA,MACA,QACAD,gFAIA,YACA,kCACA,kBAIA,YADA7pB,gCAFA6pB,qCAIA,CAEA,gCACA,qDAKA,YADA7pB,qCAHA6pB,2GACAA,+BAIA,CAGAC,gEAGaA,GAmBbD,uCACA3uB,QACAnD,YACA5X,eACAkY,OACA7X,SACA6iB,gDACAzU,0BACA0U,oBACA,+BACAtD,4DACA,6BACAA,0EAEAlM,mGACAk0B,OAEA,EACAttB,iBACAsF,yGACA,KAtCA9E,QACAnD,WACA5X,MACAK,SACA6iB,+BACAzU,0BACA0U,oBACA,+BACAtD,4DAEAA,yEACA,EACAtF,iBACAsF,yGACA,IAjBAlM,wBACAk0B,OAlEA,MAFAhoB,uCA+GA,EAEA3F,+BACA,2DACAyhB,mBAEA4O,OAGA,QAFA3nB,IACA4nB,KACAC,IAAkCA,WAA+BA,KACjE,IACAC,kBADAH,KACA,yBACA,QAA6B3nB,WAAwBA,IACrD4nB,wBAEA,CACA,SACA,QAAyB5nB,WAAwBA,IACjD4nB,gBACAG,UAGA,QAEA,EAEAzwB,4BACA,SACA,WACA,SACA,iGACA,wCACA0wB,EAEAA,iCAEA,YAAmCC,0BAAyCA,IAC5ED,2CACA,QACA,EACA1wB,sCACAA,UACAA,wBACAA,kBACAA,qCACAA,+BACA,EACAA,0BAEA8mB,aAGA9mB,6BAFAA,2BAKAA,wBACA,sDACA,yBACA,GACAgH,WACAhH,wBACAglB,aAEA,EAEAhlB,mCACA,sDACA,yBACA,GACAgH,UACAjN,sCAEA,CACA,IAGAgG,wKACAC,UAEAA,eACAoN,GACAA,sBACA3E,0BACAzI,mBACA,EACA,GAEAA,SACAtE,kBACAvK,WACAnM,KACAwe,sBACA,2BACA8b,2CACA,EACA,oCACA,2BACAA,mBACA,IAGAtf,cACApa,OAEAoa,WACA4wB,mBAEA5wB,sBACAA,SAEAA,uBACAA,gBACAA,cAEAA,cAEA,EAEAA,uBAEA,qDACAW,EACAA,kBACAzM,OACAwJ,eACAvJ,kDAEA8L,SACA,EAAiB,YACjBD,oBAEA,GAEAA,8DAEA,EAEAA,wBACAA,0CACAxL,gBACAN,OACAwJ,eACAvJ,6CAEA8L,SACA,EAAa,WACbA,SACA,EACA,EAEAD,mBACAA,sBACAC,SACA,CAEA,IAEAF,yLAuJA,MAlJAC,2BACAA,SACAtE,kBACAvK,WACAnM,KACA6rC,sBACA,2BACAvR,6BACA,IAGAtf,UACAtE,sBACAvK,WACAnM,KACA,4BACA,2BACAs6B,2CACA,EACA,wCACA,2BACAA,mBACA,IAGAtf,SACAjI,UACAuR,QACAwnB,YAEAC,yBACA/wB,8BACAA,0BACAA,oDACA,GACAA,wBACAA,YACA,wBACAA,yBACAA,uBACA,EACAA,yBACA,2BACA,eACAhM,qBACAgM,6BAGAA,6BACA,EACAA,2BACA,qCACAA,OACAA,8BAEAA,4BACA,EACAA,uBACAA,WACA,EAEAA,4BACAA,2BACA+wB,2CACA/8B,eACAgM,qBACAA,0BACA,EAAa,YACbA,sBACAA,2BACA9L,OACAwJ,aACAvJ,wCAEA,EACA,EAEA6L,0BACAA,sBACAnb,4CACAmsC,aACA,GACAC,cACAA,oBACA,EAEAjxB,gCACAA,sBACAixB,cACAA,oBACA,EAEAjxB,4BACAA,2BACA+wB,2CACA/wB,kBACAA,2BACAixB,aACA,EAAa,YACbA,qBACAA,cACAjxB,2BACA9L,OACAwJ,aACAvJ,oCAEA,EACA,EAEA6L,4BACAglB,UACAvmB,0BACAC,uFACAC,UAA2B1I,uBAA4B,CAAGA,6CAC7C2I,wBACbC,IACAmB,2BACA+wB,2CACA/wB,kBACAA,0BACA,EAAqB,YACrBA,2BACA9L,OACAwJ,aACAvJ,oCAEA,GAEA,EACA,EAGA6L,uBACAA,gBAEA,OACA6wB,2BACAD,2BACAM,4BAEAlxB,uBACAvF,4BACAuF,WACA,EACA,CACA,EAEA+N,IACA/N,cACAmxB,MAGAnxB,SACAA,WACAA,UACAqnB,WACA+J,cAGApxB,gCACA,qBACA,UACAa,yCACA8G,YACA,GACAyF,WACAnN,UACA+kB,gBACA,EAEAhlB,sBACA,0DACA,6BACA,GACAqxB,UACAN,mCACAv8B,gBACAwL,8BACA,EACA,EAEAA,gCACA+wB,gCACAA,yBAAoD/wB,eACpD,EAAa,WACbglB,UACAtnB,cACAuR,qBACAxQ,oCACAC,oDAEA,EACA,EAEAsB,yBAYAglB,UACAvmB,QAZA,2BAaAC,QAZA,oEAaAC,QAZA,EACA1I,OACA+Y,gBACa,CACb/Y,OACA+Y,WACAsiB,2BAQA1yB,OACApK,iBACA,MACA,sBACAyL,UACAU,mBACAzM,OACAwJ,aACAvJ,wCAEA,EACA,CACA,EACA,EAEA6L,uBACA,0BACA,KAEA,+BACA,WACA,GACA6uB,KACAv6B,yCACAu6B,KACAxH,MACArC,UACAvmB,gCACAC,oDACAgY,SACAnU,iBACAyM,wBACA/Y,YACAyH,cACiC,CACjC6E,eACAyM,gBACA/Y,gEAEA0I,UACA1I,OACA+Y,aACAsY,wBAC6B,CAC7BrxB,OACA+Y,mBAEyBpQ,wBACzBkwB,GAMArH,WALA,CACA3gC,WACAygC,sBACAC,mBAIA,GAEAxC,UACAtnB,cACAuR,qBACAxQ,mCACAC,kFAGA,EAAa,CACb,EACAsB,yBACA,UACA,oCACA,WACA,GAKAynB,WAJA,CACA3gC,wBACA0gC,wEAEA,CACA,CAEA,IAEAznB,6KACA0I,SAKAzI,2BAAoDuxB,mCACpDvxB,2BAAoDuxB,0CACpD,gBAIA,GAHAvxB,iDACA,OAAwCwxB,kCACxC,GACAnkB,GAEA,oBAA8CznB,MAAc,IAC5DwX,uBAAmDxX,MAAc,IACjEwO,gDACA4L,UACApa,KACA24B,aACAnhB,YAEAq0B,WAEA,EAEA,KAAU,CAEV,SAEAjoB,qBAAuC5jB,SAAoB,GAAI,YAI/D8rC,sBACAC,mBACA,GACAF,WACA,EACA,CAiIA,gBAEA,OACAxsC,mBACA2sC,kCACAC,cAGA,2BACAroB,kBAA2CvG,SAAc1c,EACzD,YACAtB,EACA+a,sCAEAA,8BACAqN,GAGArN,gCACA,6BACAA,8BACAA,yDAIA,EAAiB,YAEjB,sBADAvL,OAEAuL,qBAGA/a,EACA+a,sCAEAA,8BACAA,sBACAA,uBAEA,EACA,CAEA,kBACA,sBAA2C8xB,yBAAiCtyB,GAC5Eva,qCACAoO,sBACA2M,gCACA,OACA+xB,sBACA9sC,mBACA2sC,kCACAI,iCACAH,cAEA7xB,kDACAzZ,yCAEA,qBAA4C0c,SAAc1c,GAC1D,mCACAtB,GACA+a,iCACA/a,UACAukB,mBAAiD5jB,qBAAkC,GAAI,YACvFoa,wBACA,EAAyB,YAEzB,KAGAA,8BACAA,qBAEA3M,8BACA,EAAa,YACbpO,EACA+a,sCAGAA,6BACAA,sBACAA,uBAEA,GACApL,UACA,CAEA,cAEA,iCASA,4BARA,IACA,qCACA4V,gBACA,kCAGA,CAFA,MACA,2BACA,CAIA,CAEA,cACA,iCASA,qBARA,IACA,qCAEA,OADAzlB,cACAysC,IAGA,CAFA,MACA,oBACA,CAIA,CA/OAxxB,aAEAA,cACAvZ,iCACA4pB,gBAEArQ,aACAtE,oCACAvK,YAKAsgC,0BAGApkB,qDACAxoB,wCACAwD,IACA,GAEA2X,2CACAiyB,OACAptC,wCAEAwD,IACA,EACA,EACA,GAEA2X,qBAEAoN,sBACA,oBACAjI,cAEA+sB,cACAn7B,OACAiyB,UACAmJ,WACA/0B,SACAg1B,WAEAx9B,UACAT,UAGAsU,0BACA4pB,QACA,+CACA,oCACA,GACAryB,oBAAuC,gCACvC,cACAyI,+BACA4pB,aAEAJ,8CA9CA,IA+CAI,kBACAxtC,qCAEAwD,MACA,GAGA,EACA,EACA,GAGA2X,WACAA,qBAA+CgN,yBAC/C1nB,sDAEA0a,6BACAjJ,kBACAyI,UACAuN,sCACAhW,iBACA,EAAa,WACbA,iBACA,EACA,EACAiJ,mCAEAA,iBADA,CAAwB+xB,aAExB,EAMA/xB,4BACA,sDACAA,+CANA,YAEA,oDACA,CAKAsyB,kBAIA,cACA,6BACA,CALAC,+BAAyEC,iBAEzE,EAIAxyB,0BACA,gFAEA,EACAA,2BACA,kFACA,qDACA,EACAA,oCACA,mDACAA,gFACA,EACAA,yBACA,SACAA,gCAAgE,2BAEhE2xB,IAEA,EAIA3xB,mBAoHAA,6BACA+M,IACA,EACA/M,qCACA+M,YAA0CglB,aAC1C,EAEA/xB,sBACAyyB,YACA,oCACA,cAAwC,oCAAmD,GAE3FC,gBACAzyB,SACA,EACAD,0BAEA,QADAA,eACAoN,mBAA4C,iCAE5C,EACApN,uBACA,SACAa,+BACA,+CACA0F,iBAAwDurB,cAExD,GACA19B,yBACAE,kBACA,EACA,EAEA0L,yBACAwJ,eAAiC5jB,qBAAkC,GAAI,YACvEsO,OAAkCC,kDAClC,EAAa,YACbD,OAAmCC,2EACnCuJ,sBACAsC,qBACA,EACA,CAEA,IAEAD,oHAEAC,eACAA,2BAEAA,oBACA1L,uCACA0L,+CACA,GAEAA,kCACAhF,sCACA,EAEAgF,0BACAhF,gBACAgF,UACA,EAEAA,wDAIAA,SACAmG,KAHA,CAA4Byc,6BAG5B5iB,qCAEA,OAA6BmG,mBAC7BnG,wBALA,IAKAA,+BACAA,eACAA,2BACA2yB,+BACA,GAEA3yB,qBAEA2yB,qBACA3yB,2CAEA,sBACAA,gCAA2D,yBAE3DA,4CACA,cAAwC,OAASqH,6CAAgDlB,KACjG,cAA2C,wCAAkDnhB,IAC7F,cAAgD,qCAIhD,CAEA,IAEA+a,8EACAC,oBACAwJ,oBAAmC,CAAEopB,sBAA0BhlB,0BAC/D1Z,OACAwJ,eACAvJ,yBAEA6L,UACA,EAAa,YACb9L,4CACA,EACA,CACA,IAEA6L,uGACAC,QACAA,oBACAwJ,oBAAmC,CACnCopB,uBACAC,cACAtzB,yBACaqO,0BACb1Z,OACAwJ,eACAvJ,uBAEAgN,YACA,EAAa,YACbjN,2CACA,EACA,CACA,IAEA6L,0FACAC,oBACAwJ,gCAAgD9U,0BAA8BkZ,yBAC9E1Z,OAAmCwJ,uFACnCyD,YACA,EAAa,YACbjN,uEACAiN,YACA,EACA,CACA,IAEApB,8FACAC,2BAEAA,6BACAhM,4BACA,aACAoR,WACApF,oCAGAoF,WACApF,mCAEA3L,mDAA6EoJ,kBAC7EjJ,gBACAN,OACAwJ,eACAvJ,qBAEA,EAAiB,WACjBD,OACAwJ,aACAvJ,gDAEA,EAEA,CACA,IAEA4L,8DACAC,6BACA8yB,GACA,EACA9yB,aACAA,yBAEA,IAEAD,gIAEAgzB,SAEA/yB,cAEAA,SACAtE,gDACAvK,WACAnM,KACAguC,uBAA8C,kCAC9CxS,iBAAwC,4BACxCyS,wBACA,2BACA3T,6BACA,IAIAtf,4BAaA,OAZAJ,QACAC,YACAC,qDACAC,2BACAI,UACAD,2BACAiN,SACA+lB,kBACA,+BACA,KAGAt0B,0BACAoB,kBACA,IACA,CACA,EAEAA,2BACA,kBAAuC,SAGvC,QAFAmzB,gCACAC,KACAz+B,IAA2BA,uBAA+BA,KAC1D,uBAEA,GADAy+B,oCACqC,KACrC,CACA,UACAD,sBACAA,8BACAA,gBACAA,oCACAA,qBACA,EAEAhyB,oBACAlI,aACA+G,gDACA,EAAa,IAEb,IAEAD,4DAEAC,YACAA,WACAA,SACAtE,cACAvK,WAGA,IAEA4O,mGACAC,eACAoN,GACAA,sBACA/Y,oDACAG,iBACA,oBACAwL,qDACAA,4BAEAA,yBACAA,0BAEA,EACA,GAEAA,mBACAC,SACA,CACA,IAEAF,sJACAC,UACAqzB,kBACAC,kBACAC,iBACAC,oBACAC,qBACAC,eACAC,mBAEA3zB,eACAA,mBACAA,eAEAnb,mBACAuoB,sBACA3E,0BACAzI,mBACA,EACA,IAGAA,YACAA,eACAyI,0BACAzI,mBACA,IAGA,iBAEA,QADA4zB,KACAj/B,IAA4BA,yBAAiCA,IAC7Di/B,4BACAA,QACAA,+BACAA,QACAA,gCACAA,QAEA,QACA,EAEA5zB,wBACA6zB,SAAiCC,6CACjC,EAEA9zB,wBACA,6BAGAnb,4CACAmb,eACAA,eAiBA,QAfA+zB,cACA,UAA8BtuC,kFAC9B+O,gBACAwL,sCACA,EAAiB,YAEjBA,6BADAnb,oBACA,wBAGA,4BAEA,EACA,EAEAia,KACAnK,IAA4BA,yBAAiCA,KAC7D,WACAmK,SACA,CAEA,mCACAkB,eACAA,cAEA,yCACA9L,OACAC,6DACAuJ,eAEAuC,UAEA,GACA7L,QACA,EAEA4L,mBACAC,UACAY,mDACA,CAEA,IAEAd,qHAIA,WACAjM,wBACAG,yCACA+/B,GACA,EAEAh0B,8CACA/L,iCACA+/B,GACA,GAEAlgC,YAEAA,8BACA,eACA+L,YACAC,qDACAC,8BAEA,CACA,EAEAC,WACAA,oFAEA,wDAGA,aACA/L,6BACAA,YACA8I,yCACAk3B,qDACA/2B,QAA4BoT,0CAC5BnT,SAAuCmT,6CACvC4jB,WAEA92B,OAAyBkT,2CAEzBhc,aACAA,sCAEA6/B,oCACAlgC,oDACAA,2CAEA,GAGAuV,qBAAqC5jB,SAAmB,YACxD,2BAA6D,qBAC7DqO,0BACAqc,gBAEArc,gDAA0E,qBAAoBsE,OAC9F67B,wBACAngC,gFACAA,8CACAA,+CACAA,6CAEA,EACA,GACAA,kBACAiJ,QACAm3B,QACAC,SAIA9qB,sBAAsC5jB,SAAmB,YACzD,2BAKA,SACAsX,sBAEA,sBAGAhU,eACAA,gBACA+K,iCACAA,6CACAA,2CAGAsC,8BACAtC,4BAIA,EACA,EACA,CAtEA+L,wBAwEA1L,wCAGAigC,oCACAv0B,kBACA,EAAa,YACbhM,eACA,GAEAF,cACA,iEACA,+DACA,4CAGAA,eAEA0gC,WACAxlB,6BACAD,iCAEAwP,SAEAvP,4BACAD,4BAEA0lB,WACAC,+DACA1lB,oBACAD,kBAEAnH,OACA8sB,gCAAyEz2B,WAAY02B,sBACrF3lB,4BACAD,wBAEA6lB,iBACAF,iBACA1lB,2DACAD,mBAEA8lB,SACAH,iBACA1lB,oEACAD,mBAEAod,MACAuI,oBACA1lB,gBACAD,mBAEA5E,aACAuqB,mBACA1lB,gCACAD,wBAEA+lB,aACAJ,oBACA1lB,sCACAD,wBAEAgmB,UACA/lB,oBACAD,oBAEAimB,MACAN,2BAAoE3F,OAAQkG,sBAC5EjmB,sBACAD,0BAEA,iBACAC,iCACAD,6BAEAxN,OACAyN,+BACAD,mBAEAmmB,QACAR,oBACA1lB,qBACAD,4BAEA,aACA2lB,aACA1lB,wBACAD,sBAEA,gBACAC,qBACAD,oBAEAhS,OACAiS,iBACAD,0BAEA7R,QACA8R,qBACAD,oBAEA3R,OACA4R,iBACAD,6BAEA,iBACAC,4BACAD,oBAEA,gBACAC,yBACAD,6BAIA,IAGAhP,oIAEAC,SACAA,WAIAA,WAEAA,UACAm1B,QACAC,YAEAp1B,yBACAA,cACAA,4DACAA,kCACA9W,OACAuf,mDACAvf,OACA,EAEA,EACA,EACA8W,SACAtE,kBACAvK,YAEA6O,gCACAA,0BACAA,sDACAA,+BACAA,gBACA,GAAS,GAETA,sCACA,CAAcpa,wBACd,YAEAoa,eACAsQ,eACA+kB,WApCA,KAsCAn4B,sBAEAhU,+BACA,GACA8W,eACA,EAEA,IAEAD,+GACA,mEACAC,cACAs1B,WAEAt1B,gBACAA,eACAA,eACAA,4BACA,YACA,qCACA,kCAAkE,EAClE,EACAA,kBACAA,SACAA,WAEA,SAEAsoB,aACAtoB,+BAAuDpa,SAAoB,YAC3EsX,sBACAq4B,UACA9sB,mDACAzI,oBACAjY,uBACA8M,gBAAkD,WAClDmL,oBACA,EAEA,EACA,EACA,EACA,EACAsoB,IAEAtoB,SACAtE,gBACAvK,WACAnM,KACAY,eAEA,2BAA8D4d,UAC9D,yBACAD,+BACA,IAIAvD,wBACA2H,+BACA3H,mBACA,EACAA,kBAEAA,oBACA9W,IACA8W,oBACAA,uBAEA,0BAEAw1B,KACAllB,MACAklB,YACA/2B,sBACAC,qEACA4R,sEACA3R,UACA1I,OACA+Y,cACAsY,wBACiC,CACjCrxB,OACA+Y,mBAEqBpQ,QAErBxK,2BACA,MACA,SACAqhC,KAiBA,GAhBA5wC,mDACA6wC,oBACAH,kBACAI,OACAJ,eAA0D,YAC1D,6BACArsC,CACA,EAAiC,YACjC8K,eACAyhC,KACAz1B,2BACAA,cACA,IAGA,IACA21B,gBACAvhC,0BACAE,mBACA0L,oBACA,6BACA,8BACA41B,yBADAA,CAEA,EAAyB,GACzBzhC,KACAshC,gBACAI,IACA1hC,yDACAshC,IACAthC,+CACAD,OACAwJ,yBACAvJ,OAEA,EACA,CACA,EACA,EAEA6L,sBACAA,+CAA0E,4BAC1EA,SACA,EAEAA,8BAMA,OALAJ,QACAC,YACAC,gEACAC,oCAEAnB,0BACAtK,mBACAg0B,GACA,IACA,CACA,EAEAtoB,sBACAA,kBACA1L,aACAA,4CACAwhC,6BACA91B,kBACA81B,0CACAzhC,sDACAG,iBACA,gBACAiU,0BACAzI,uBACAjY,MACAguC,sBACAr2B,kBACAs2B,4BAEA,EACA,EACA,GAEA,GAGAh2B,2BACAA,qBACA1L,gBACAA,yCACA,kCACA0L,qBAEA,oCAEAnb,wBACAyQ,2BACAjB,sDACAG,iBACA,wCACAyhC,sCACAhzB,YACAwF,0BACAzI,4BACAjY,MACAguC,wBACAr2B,UACAs2B,eAEA,EACA,EACA,GAGA3hC,2DACAG,iBACA,6CACAyhC,2CACAhzB,YACAwF,0BACAzI,4BACAjY,MACAguC,wBACAr2B,UACAs2B,eAEA,EACA,EAEA,CACA,EAGA,IAEAj2B,6KACA9G,aAEA,YACA+G,SACAk2B,WACA/uB,oBACAmJ,UAEAtQ,wBAEAA,wBAEA,GADAhM,eACA2T,GACA3H,sBACA,oCACA,gCACA,GACAm2B,WACA,MACAA,SAEA,EACAn2B,aACAA,2BAA8C,kCAC9CA,WACAwJ,qBAAqC5jB,SAAmB,YACxDwX,sBACA4C,wBAIAyI,uCACAzI,iBACA,GAEA,aACAA,wBACsB,aACtBA,2BACsB,aACtBA,0BAEAA,uBAEA,EACA,GACAA,yBACA,QAAqBnM,mBAAkBe,4BACvC,EACAoL,SACAtE,yBACAvK,WACAnM,KACAY,eACA,yBAA0D4d,UAC1D,yBACAD,+BACA,IAGAvD,2BACA,uBACAA,mCACA,8CACA,WAHA,EAIA,EACAA,yBACA,oBACApL,cACAA,aAA+B,YAC/BA,cACAN,mBACA2E,oBACA+G,sBACA,4BACArL,4BACAX,mCACA,EAAqB,IACrB,EAAiB,YACjBE,OACAwJ,aACAvJ,uGAEAS,aACA,KAGA,CACA,EAEAoL,0BACA,qBACAJ,QACAC,YACAC,0DACAC,uDACAC,UACA,GACAE,sBACiBtB,wBACjB,uBACAhK,gBACA,SACAqB,gBACAwa,2DACA7b,yBACAA,gBACAN,kBACA,EAAyB,YACzBJ,OACAwJ,aACAvJ,wGAEAS,eACA,EACA,CACA,IAGA,CACA,EAEAoL,yBACA,oBACApL,cACAA,aAA+B,YAC/BN,kBACA,EAAiB,YACjBJ,OACAwJ,aACAvJ,sGAEAS,aACA,KAGA,CACA,EACAoL,kBACAA,kBACA1L,aACAA,4CACAwhC,qBACA91B,kBACA81B,kCACAzhC,sDACAG,iBACA,gBACAiU,0BACAzI,mBACAjY,MACAguC,sBACA5T,sBAEA,EACA,EACA,GAEA,GAGAniB,uBACAA,qBACA1L,gBACAA,yCACA,8BACA0L,qBACA,gCACAnb,wBACAwQ,2BACAhB,sDACAG,iBACA,gBACA4hC,sCACA3tB,0BACAzI,wBACAjY,MACAguC,mBACA5T,OAEA,EACA,EACA,GAGA9tB,2DACAG,iBACA,gBACA4hC,2CACA3tB,0BACAzI,wBACAjY,MACAguC,wBACA5T,OAEA,EACA,EAEA,CACA,GAGA,OACA,CAAgB5f,eAChBgH,eACAwF,kBACAsnB,oDACAC,wGACA3P,eACA,wBACA,WACA3mB,+BACAA,kCACAu2B,KACA,GACAA,EACA,EACA3P,cACA9yB,cACA0E,kDACAwH,sBACAA,+BACAlM,kBAEA,EAAqB+U,YACrB7I,yBACA,GAEA,CAAgBuC,cAChBgH,cACAwF,uBACAsnB,kDACAC,uGACA3P,eACA,wBACA,WACA3mB,+BACAA,uCACAu2B,KACA,GACAA,EACA,EACA3P,cACA9yB,cACA0E,kDACAwH,sBACAA,6BACAlM,kBAEA,EAAqB+U,YACrB7I,yBACA,GAEA,CAAgBuC,cAChBgH,cACAwF,kBACAsnB,mDACAC,uGACA3P,eACA,wBACA,YAEA3mB,gCACA1L,OAA2Ci9B,oCAC3CvxB,4CACAu2B,KACA,GAEAA,EACA,EACA3P,cACA9yB,cACA0E,kDACAwH,sBACAA,6BACAlM,kBAEA,EAAqB+U,YACrB7I,yBACA,IAGAA,oBACAA,+BACAA,0BACAw2B,sBACAx2B,wBACA,EACA,EACAA,sBAEAA,6BACAA,qBACA,EAEAA,gCAEA,wBACA7L,sHAEAyL,QACAC,YACAC,gEACAC,uDACAC,WACAA,iCACAA,QACAA,2BACAA,mBACA,GACAE,sBACatB,wBACb,uBACA,SACA5K,eACAiC,gBACAwgC,2DAEA,SACA5xC,oCACA,uBACA,eACA,cACAmb,iCACA02B,OACA12B,yCAEA,MACA,aACAA,+BACA02B,OACA12B,uCAEA,MACA,aACAA,+BACA02B,OACA12B,8BAAuE,mBACvEA,uBACA/G,oBACA+G,sBACA,4BACArL,4BACAX,mCACA,EAAyC,IACzC,GAAqC4Z,UAKrC,GACAxZ,yBACAE,mBACAJ,OACAwJ,eACAvJ,mBAEAgiC,SACA,EAAqB,YACrBA,UACAjiC,OACAwJ,aACAvJ,cAEA,EACA,CACA,EACA,CACA,IAEA4L,oGAyBA,aACAyJ,0BAAsCoE,0BACtC+oB,sBACA32B,wCACA,GAEA5L,OACAoV,oBAAwCotB,iCAA6ChpB,SACrFpE,oBAAwCotB,oCAAgDhpB,SACxFpE,wBAA6CotB,iBAA2BhpB,WACxEpZ,iBACAwL,oBACAyhB,gDACAzhB,mCACA62B,6EACA,GACA72B,8CAA6E,sBAC7E,EACA,EACA,CA3CAA,SACAtE,kBACAvK,YAEA6O,eACAtE,aACAvK,YAGA6O,WACA82B,QACAC,gBAEA/2B,+CACAA,4BACA1L,oCACA0L,wBACA,GACAg3B,GACA,EACAh3B,sCAA+Di3B,eAA8BH,gBAC7F9iC,uBAEAgM,aAA6Bk3B,8BAqB7BF,GAEA,IACAj3B,sHACAC,gBAEAwsB,yBACAA,iCACA2K,qDACAA,WACA1uB,kCACAzI,+BACA,GAEAA,mFAEA,EACA,GAEAA,SACAtE,mDACAvK,WACAnM,KACA0Y,iBAAwC,uCAGxC,IAEAqC,8JACAC,cAEAA,WACAA,QACA,IACAo3B,4BACAC,YAFA,OAEA,GACAC,WAWA,GAVAt3B,4BACA,YAA0BrL,IAAOA,IACjC,WACA,SAGA,QACA,EAEA9P,6CACAmb,iCACA,gDACAu3B,4BACAv3B,8CACA,MAEAA,4BAEAA,+BAEAA,cAEAA,oBACA1L,4BACAE,gBACAN,OACAwJ,eACAvJ,sDAEA8E,aACA0f,mBACA,EAAiB,IACjB,EAAa,WACbhb,mBACA4qB,IACAr0B,OACAwJ,aACAvJ,4FAEA,EACA,EACA6L,mBACAnb,mCACA,EAEAmb,yCACAxH,2DACA,yDACA,GAHA,KAKAwH,oCAAwDw3B,WAAYC,aAAaz3B,+BACjF,IAEAD,oIAOA,aACA,gCACAC,uBACAA,6BACAA,gBACA,EACA,CAZAA,iBACAA,SACAtE,kBACAvK,YAUAumC,IACA13B,WAEAA,sBACAA,gBACAA,wBACAglB,iCACA0S,GACA,EACA,EAEA13B,uBACA,0BACA,KAEA,+BACA,WACA,GACA6uB,KACAv6B,yCACAu6B,KACAxH,MACArC,UACAvmB,gCACAC,oDACAgY,SACAnU,iBACAyM,wBACA/Y,YACAyH,cACiC,CACjC6E,eACAyM,gBACA/Y,gEAEA0I,UACA1I,OACA+Y,aACAsY,wBAC6B,CAC7BrxB,OACA+Y,mBAEyBpQ,wBACzBkwB,GAMArH,WALA,CACA3gC,WACAygC,sBACAC,mBAIA,GAEAxC,UACAtnB,cACAuR,qBACAxQ,mCACAC,kFAGA,EAAa,CACb,EAEAsB,yBAYAglB,UACAvmB,QAZA,gBAaAC,QAZA,qDAaAC,QAZA,EACA1I,OACA+Y,gBACa,CACb/Y,OACA+Y,eACAsiB,2BAOa1yB,wBACbA,GACAmyB,eAEA,EACA,CACA,IAEAhxB,iIACAza,OAGA,wDACA0a,0BAEAA,SACA23B,SACA1vB,OACA7B,YAEAwxB,QACA3vB,OACA7B,YAEAyxB,UACA5vB,OACA7B,YAEA0qB,UAIA9wB,wBACAmF,iBAEAnF,cADAA,iBACAA,qBACAA,uDAGAA,eAEAA,kCACApL,QACAA,4BAAmE+L,aACnE,GACAX,WACA,EACAA,sBACAA,SACAtE,oBACAvK,WACAnM,KACA8yC,sBACA,2BACAxY,6BACA,IAGAtf,+BACA,iBACAA,wBACAA,kDACAA,+BACAA,cACA,GAAS,GAET,WACA+3B,MACAC,8BAWAxuB,qBAAiC5jB,4BAA0C,YAC3E,OAA6BqyC,qCAC7BC,sBAEA,kBACA/yB,IAbA,YACA,UACA,sBACA6yB,CAGA,CAOAG,uBACAn4B,uBAGApL,kBACA,oDACAA,4BACA,EACA,GACA/P,gCACAmb,2BACAA,wBACAsQ,4BACA+kB,WAnEA,KAsEA,GACAr1B,cACAA,YACA,EAEA,IAEAD,4FAEAC,aACAA,YAEA2rB,iCACA3rB,UACAnb,gCACAilB,SACArB,uCACAqB,gBACAA,uBACA,GAEA9J,cACA,GACAA,aACA,gCACA,GAEAA,SACAtE,aACAvK,YAGA6O,wBACA2rB,eAEA,QADAtkB,KACA1S,kBAAiDA,KAAQA,IACzD,yBACA0S,IACA,KACA,CAGAA,MACArH,sBACA/G,aACA+G,kBACA,EAAiB,KAGjB,CAEA,IAEAD,kGACAC,qBACAsuB,0BACAtuB,eACA,EACA,EAEAA,gBAEAA,kBACAA,+BACAsuB,sCACAA,0BACAtuB,eACA,EACA,EACA,EACAA,SACAtE,cACAvK,YAGA6O,uCACAsuB,0BACAtuB,eACA,EACA,EAEA,IAEAD,qIACAC,UAEAA,SACAtE,cACAvK,YAGA6O,aAEAA,qBACAA,gCACAA,YACA,EACA,EACAA,qBACAA,kCACAA,WACA,EACA,EAEAA,oBACA,QACAJ,QACAC,YACAC,0DACAC,uDACAC,WACA+tB,eACAqK,YACAC,WACAC,YACApV,aACAqV,cAGAv4B,WACAA,qBACA,SACAnb,8CACAoR,GACAo4B,SAEA,GACArwB,4BACAw6B,kCACAv4B,UACAu4B,WACA,EACA,CACA,GACAt4B,yCAEA,EAEAF,qBACA,QACAJ,QACAC,YACAC,0DACAC,uDACAC,0BACA,iCACAA,2BACAquB,sBAAgDruB,gCAEhDA,WACAA,qBACA,SACAnb,8CACAoR,GACAo4B,SAEA,GACArwB,4BACAw6B,kCACAv4B,UACAu4B,WACA,EACA,CACA,GACAt4B,yCAEA,CAEA,IAEAH,4GACAC,SAAyBtE,iCACzBsE,aACAA,6CAEAA,qBACA6tB,6BACA7tB,YACA,EACA,EAEAA,sBACA,mDACA,EAEAA,oBACA6tB,2BACA,YAA8Bl5B,oBAA0BA,IACxD,2BACAqL,uBACA,KACA,CAEA,EACA,EAEAA,sBAEA,8BAAiDrL,KAAMA,KACvD,QACAk5B,uCACA7tB,sBACA,EACA,CACA,CACA,IAEAD,mJACAC,iCACAA,+BACAA,aACAA,qDACAA,mCAEAA,yBACA,eACA,EAGAA,qBACA6tB,8BACA7tB,aAEAA,kCACA,EACA,EAGAA,yBACA6tB,kCACAA,gCACA35B,OAAkCC,wDAClC,EACA,CACA,IAEA4L,8EACAC,eACAA,iBACAA,UACA,IAEAD,kHACAC,SAEAA,qBACAA,WACAA,YAEAA,8BACAA,wCACAA,8BACAA,WACAA,yCACAuY,QACAvY,WAEA,GAAa,EACb,EACAA,YAEAA,iEAEAA,oBACAA,YACA,SACAnb,sCACA,oBACAoR,mBACAwiC,OAMA,GACAnkC,iCACA0L,YACAA,YACA9L,OAAkCC,yDAClC6L,aACA,EAAa,WACbA,YACAA,WACA,EACA,EAEAA,oBACA,eACAH,YACAC,0DACAC,8BACAG,iCACAC,UACAgN,SACAurB,sBAA6C,kCAC7C72B,kBAAyC,qBAGzC,CACA,CACA,IAEA9B,gGACAC,WACAK,UAEAL,YAIAA,yBACAA,oBACA,EACAA,0BACA,6CAEAkJ,SAGAjJ,UAEAD,WACA7L,cACAuJ,eAKA,CAEA,IAGAqC,oHACAC,SAAyBtE,mCACzBsE,aACAA,YAEAA,qBACApC,QACA+6B,6BACA34B,YACAA,wDAAoG,0BACpGpC,WACA,EAAa,YACb1J,mCACA0J,WACA,EACA,EAEAoC,iCACAuY,QACAvY,wDAAgG,0BAChG,GAEAA,sBACA,2BACA,EAEAA,yBACA,0CACA,EAEAA,wBACA,2BACA,EAEAA,sBACA24B,6BACAtgC,oBACA,EAAa,YACbnE,oCACA,EACA,EAEA8L,qBACA24B,4BACAtgC,gBACA,EAAa,YACbnE,mCACA,EACA,EAEA8L,wBACA,yBACA,EAEAA,yBACA,8DACA,EAEAA,sBACAA,gBACA24B,4BACA,YAA8BhkC,gBAAsBA,IACpD,uBACAqL,mBACA,KACA,CAEA,EAAa,YACb9L,mCACA,EACA,EAEA8L,gCACA,SACApC,QACAoC,2BACAA,iCACAlB,mBAEA,GACA1K,0BACAwJ,YACAoC,WACA,EAAa,YACbpC,YACA1J,mCACA,EACA,CACA,IAGA6L,4GACAC,aACA,QAEAwJ,wBAAoC5jB,UAAYgoB,0BAChD5Z,eACAgM,YACAA,gBACAA,iBACAA,kBACAA,gBACAnB,sBACAmB,6CACAA,+CACAA,kDACAA,8CACA,GACAA,YACA,EAAS,YACT9L,kEACA8L,YACA,GAEA,SACAA,yBACA,qCACA44B,GACAvkC,0EAGA2L,gBAFAkO,iBAEA,IADAA,cAEA,GAEA0qB,MACA,EAGA,CACA,IAEA74B,uFACAC,YACA,YACA/G,aACAQ,cACA,EAAa,IAEb,IAEAsG,sHACAC,QAEA,IACAA,2CAIA,CAHA,MAEAA,eACA,CAIA,GAFAA,yCAEA,uBACA,MACA,IACA64B,oBAIA,CAHA,MAGA,CACA74B,oBACApC,OACA4L,iBAAyC5jB,kBAA4B,CACrEugB,mBACA2yB,iCACAzoB,cACAC,gBACqB1C,SACrB,6BACApZ,iBACA,IACAwL,2CAIA,CAHA,MAEAA,eACA,CACA,EACA,EAEAA,sDACAA,kDAEAA,UACAA,gDACAA,wBAA2C+G,0DAC3C/G,qBACAA,sBACAA,UACA,EAEAA,SACAtE,SACAvK,YAGA6O,6BACAuY,YACAvY,UAEA,GAAa,GAEbA,4BACA,yBACAwJ,wCAA6E5F,0BAC7E,EACA5D,6BACA,+CAA6E4D,eAC7E,EAEA5D,2BACApC,QACA4L,2BAA+C5jB,kBAA4B,IAAIgoB,0BAC/EhQ,YACA1J,OAAsCC,gHACtC,EAAiB,YACjByJ,YACA1J,+CACA,EACA,EAGA,YAEA8L,UAEA,CAEA,aACAA,iBACA/G,aACAQ,cACA,EAAa,KAEb,IAIAsG,+EAEAH,QACAC,YACAC,sDACAC,2CACA1L,uCACA,aACA0d,KAGAA,EAFAzsB,oCACAA,oDACA8b,SACA,qBACA23B,kFACA/4B,cACA,EACA,GACAE,8BAGAiB,iBACAA,kCAEAA,gBACA,IAIApB,gIACA,4BAIAC,kBACA,SACA,8BACAg5B,QAA6Bz2B,6BAE7BvC,kCACA,iCACAi5B,6BACA,cACAC,MACA,CACA,GAEAl5B,iCAAkDnF,8CAGlDA,gDACAmF,qHACAnF,0CACAmF,6FAGAA,yCADAA,qCACAnb,qDAGA,GAGAmb,kEAEAA,kDAEAA,2CAEAA,WAGAA,WACAm5B,OACAz9B,SACA8b,YAEA4hB,UACA19B,SACA8b,aAIAxX,0BACA,cACAA,+GACa,sBACbA,mGAEA,EAEAA,sBACAq5B,eACAr5B,sBACAA,uBACA,EAEAA,wBAGA,iBADAs5B,gEACA,oBAFAA,mBAGA,EAEAt5B,wBAIA,SAEA,yEACAs5B,8CACA,GACAC,CACA,EAEAv5B,gDACAA,8CACAnF,gCAGAmF,oFAEAA,YACAvM,SACAub,SACAwqB,UAGAx5B,6BACAA,4EACA,EAEAA,6BACA,4EACqBR,qBAAmCA,OACxD,EAEAQ,2BACA,iBAIA,EAEAA,0BACAA,oBACAA,oBACAA,mBACA,EAEAA,gBACAy5B,YACAzqB,SACA0qB,aAIA15B,UACAy5B,YACAzqB,SACA0qB,aAIA15B,wBAGA,6CADA25B,QACA,qBAFAA,SAGA,EAEA35B,wBACA,SAEA,6CACAA,yDACA25B,wBACA,KAGAA,sCAEAJ,CACA,EAEAv5B,sBAEAA,EADAsX,WACAmiB,oBACAz5B,0BACA,EAEAA,2BACA1a,0BACA0a,4DAGAA,sBACA1a,0BACAA,6CACA0a,gCACA,GAGAA,aACAla,OACA8zC,aAEA55B,uBACAA,mBACAA,uBACA,EACAA,0BACA,yDACA,EACAA,wBAEA,aAMA,EAEAA,4BAEAA,wBACA,OACA65B,cACAn8B,OACAo8B,WACAC,cAEA,YACAC,wBAEAA,qBACAA,yBACAA,0BACAA,wBAEAh6B,2DACA,EACAA,uBACAA,+DACA,EAEAA,aACAi6B,mCACAC,iBACAC,qCACAC,oCACAC,sBACAC,qBACAC,oBACAC,sCACAj3B,iBACAU,6CAEAjE,mDAEAA,gBACAm1B,OACA9J,QACA/C,kBACAx0B,aACAmF,aACAnF,YACA,EAAiB+U,WACjB,GAGA7I,0BAEAA,aACAA,iBACA,oCAEAy6B,KAGA,4DAEA,oDAEA,uCACA,IACA,sBACA,0CACA,gBACA,qDACA,0BAKA/2B,GAOA,8FACA1P,eACAA,eAEA1O,cAEA6b,aAGAu5B,IACAp1C,eAEA0a,+CAMA,CAJA,MAAsB9a,GACtB8a,kBACAA,gDACAy6B,IACA,CAEA,CACA,GAIAE,0BADA,mFACA91C,gDAGA,KAGA81C,iCACA9/B,8BAKAhW,yHACA,qDACA,oCACA+1C,wBACAA,0BACA56B,iBACA26B,+CACA36B,sDAEAA,oDAEA,CACA,GAKArC,iCACAwD,WAEApH,wBAEA0gC,GACAn1C,iBAIA,EAGA0a,wBAGAA,qCADA,yGACAnb,2DAGA,KAGAS,2CACA4O,OACAC,mDACAuJ,iBAGAsC,iCACA,GAAa9K,kBACb8K,UACA9L,OACAC,iHACAuJ,cAEA,EACA,EAEAsC,2BAcAA,0BACAglB,UACAtmB,QAbA,sFAcAD,QAfA,UAgBAE,QAdA,EACA1I,OACA+Y,mCACAsY,kCACa,CACbrxB,OACA+Y,aACAsY,2BAQa1oB,OACbxK,YAEAI,iBACAoK,GACAuC,aAEA,EACA,EAEA,2CACAnB,eACA66B,wMAEAC,6LAIAhlC,yMAEAglC,0EAIA,IACA/6B,2EACAg7B,YAEA,IACAh7B,8GACAg7B,gBACA,IACAh7B,iIACAC,oBACAA,WACAA,uBACA,+EAKAa,gCAAiDnC,8EAIjD,QAHAs8B,IACAhS,IAEAvhB,IAAoCA,WAA0BA,IAC9D5G,QACAnD,cACA5X,2BACAijB,SACA5iB,SACA6iB,+BACAzU,0BACA0U,oBACA+xB,GACA,EACA36B,iBACA2oB,GACA,IAMA90B,MADA80B,IACA,CACAtrB,aACAvJ,sEAGA,CACAuJ,eACAvJ,8BAGA8L,UACAY,kCApCA,MAFA8E,gEAwCA,CACA,IACA5F,0HAEAC,SACAA,WAEAA,yBACAA,iBACAT,8DAGAS,qCACAxL,gBACAwL,2BACA9L,OACAwJ,eACAvJ,uCAEA8L,SACA,EAAiB,WACjBjM,+CACAgM,gBACA,EACA,CACA,IAEAD,2EACAC,8DACAA,eACAA,kCAEA,IACAD,2EACAC,8DACAA,eACAA,6BAEA,IACAD,+FACAC,gCACAA,2BACA,IAEAD,uGACAC,cAEA8oB,gBACA90B,oDACAmN,mBAEA2nB,QACAmS,+CACA95B,iBACA,EAAS,YACTA,iBACA,EAEA,IACApB,0HACAC,cACAhM,6CAEA80B,gBACA90B,oDACAmN,cAEA2nB,QACAmS,+CACA31C,cACA6b,YACA,EAAS,YACT,QACAA,aACAnB,SACA,EAEA,IAEAD,4DACAC,QACAk7B,cACAx9B,gBACAy9B,cACAC,WACAC,qBAGA,qCACAr7B,wBACA,yCAEA,kDACAA,2BAEAA,2BACA,wCACA,EAEAA,iBAGAmB,KAFAm6B,iBAEA,GAAiC,CAAEhT,sBACnC,EAEAtoB,4BACAkiB,iCACAA,uBACA,EACA,EAEAliB,yBACA,yBAEAnb,gCACA,uBACAq9B,iBACA,GAEAA,iBACAA,kCAEAA,CAEA,EAGAliB,2BACA,SACAu7B,KACA,6BACAxpB,8BACAA,kBACAjsB,UACA+d,kBACA23B,oBACAC,sBACAC,0BACAC,8BACAC,kCACAC,gCACAC,sBAIA/pB,iDACAA,iDACAA,2CAEA,4CACAwpB,MACAA,4CACAA,sCACAA,sCACAA,kCAGAA,OACAQ,UAEA,GAEAjmC,sBAEAic,gBADAA,mBACA,+BACAA,gBACA,wBACAA,mBACA,8BAEA,GAEAA,iBACA,GAEAgqB,CACA,EAEA/7B,0BACAa,qBACAm7B,SACA95B,6BACAzI,uCACA,EAEAuG,0BACA,SACA,gCAEAjK,iCAEAA,sBACAA,yBAEAkmC,YACA,CAEA,IAEAl8B,mFAyBA,GAxBAC,SACAA,WAEAA,mBACAA,gBACAA,gBACAA,gBACAA,gBACAA,YACAA,gBACAA,cACAA,gBACAA,eACAA,eACAA,eACAA,cACAA,cAEAA,qBACAA,2BACAA,iBACA,EACAA,UAEA8oB,QACA,IACAjkC,yCAGA,CAFA,MACAmP,gCACA,CAGA,UACA,IACAnP,yCAIA,CAHA,MAEAmP,+BACA,CAIAgM,oBACA,SACAnb,oCACA,kBACAq3C,OACA,GACAl8B,iBACAk8B,iCAEA,wBAAoCl8B,QACpCm8B,6BACAC,yBAGAj7B,iCAA+Cjb,gDAC/C,CAUA,IAEA6Z,kIACAC,gCACAA,gCACAA,cACAA,SAAoBtE,0BAEpBsE,sCA2BAA,oBACA,MACA,uBACAq7B,oBACAr7B,uBAEA,yBACAq7B,oBACAr7B,2BAGAq7B,WACAr7B,0BAOApC,qCACA1X,4BACAm2C,gBACSzuB,4BACT9X,2BACA,uCACAic,cAnDA,YAEA,mBACA,eACA,SACA,sBACA0mB,KACAphC,KAgBA,OAfAilC,sBACA,mBACAjlC,WACA,sBACAklC,yBACA9D,mBACA,GACAphC,UACAohC,6CACAphC,mBACAohC,kCAEA+D,iBACAA,WAGA,CA2BAC,cAEA,GACAz8B,4BACA,4CACAA,4BAGA,EACA,EAEAA,WAEAA,2BACA+R,gBACA/R,cACAA,gBACAA,0BAGAA,WACAA,wBAEAA,6BAEAA,cACAA,aACAA,wBACAA,sBACAA,+CAEA,EAEAA,yBACAA,aACAA,yBAEAA,cACAA,eACA,EAEAA,+CACAA,iDAEAA,8BACA,SACAnb,oCACA,kBACAq3C,OACA,GACA/6B,kBAAqCjb,qDACrC,CAEA,IAEA6Z,2GAQA,GAPAC,WACAA,UAEA8oB,UACA9oB,iBAGA8oB,OACA,IACAjkC,yCAIA,CAHA,MAEAmP,+BACA,CAGAgM,qBACAmB,2BAAyCjb,eACzC,EAEA8Z,cACAA,SAAoBtE,0BACpBsE,cAEAA,SACA08B,eACAC,gBACAxH,QAGAn1B,oBACA,OACA28B,kCACAC,mDACA/4B,mBACA,sBAGAilB,UACA/e,0BAEAnM,oCACA1X,0BACS0nB,2BACT5N,SACAA,+CACAA,2CACAA,oCAEA,sBACAA,iCACA,eACA,aAEAA,iBACAvM,cACAub,MACApE,gCACAA,sCACArH,0BAEA,EAEA,EACA,EAEAvD,WAEAA,2BACA+R,gBACA/R,cACAA,gBACAA,0BAGAA,WACAA,wBAEAA,kCAEAA,cACAA,aACAA,wBACAA,sBACAA,+CAEA,EAEAA,yBACAA,aACAA,yBAEAA,cACAA,eACA,EAEAA,+CACAA,iDAEAA,8BACA,SACAnb,oCACA,kBACAq3C,OACA,GACA/6B,kBAAqCjb,qDACrC,CAEA,IAEA6Z,8GAMA,GALAC,WACAA,UAEAA,oDAEA8oB,OACA,IACAjkC,yCAIA,CAHA,MAEAmP,+BACA,CAGAgM,qBACAmB,8BAA4Cjb,SAC5C,EAEA8Z,cACAA,SAAoBtE,0BAEpBsE,SACA08B,eACAC,gBACAxH,QAGAn1B,oBACA,OACA28B,kCACAC,oDAGA9T,UACA/e,0BAEAnM,wCACA1X,0BACS0nB,2BACT5N,SACAA,+CACAA,0CACA,EACA,EAEAA,WAEAA,2BACA+R,gBACA/R,cACAA,gBACAA,0BAGAA,WACAA,wBAEAA,6BAEAA,cACAA,aACAA,wBAEA,EAEAA,yBACAA,aACAA,yBAEAA,cACAA,eACA,EAEAA,+CACAA,iDAEAA,8BACA,SACAnb,oCACA,kBACAq3C,OACA,GACA/6B,kBAAqCjb,qDACrC,CAEA,IAEA6Z,kGAGAC,+DAEAA,oBASApC,oCACA1X,qBATA,CACAy2C,eACAC,aACAC,yCACAC,qCACAj5B,uBAKS+J,2BACT5N,mBACAA,+DACAA,wDACA,EACA,EAEAA,gCACAuY,GACAvY,UACA,GAAK,GAELA,kCACAmB,qCACAnM,qCACA+nC,iCACAzsB,mCAEA,CACA,IAEAvQ,yGAEAC,cACAA,SAAoBtE,0BAEpBsE,SACA08B,eACAC,gBACAxH,QAGAn1B,oBAQApC,oCACA1X,qBARA,CACA22C,4BACAC,wBACAH,kCACAC,uDAKShvB,2BACT5N,SACAA,+CACAA,2CACAA,mCACA,EACA,EAEAA,WAEAA,2BACA+R,gBACA/R,cACAA,gBACAA,0BAGAA,WACAA,wBAEAA,6BAEAA,cACAA,aACAA,wBACAA,sBACAA,+CAEA,EAEAA,yBACAA,aACAA,yBAEAA,cACAA,eACA,CAEA,GAKA,CA/4NA,GCIAnb,qCAEAyZ,yBACA,mBACA,6CAA2C,GAC3C,CACA,GACAA,sBACA,mBACA,6CAA2C,OAC3C,CACA,GAEAA,4BACA,mBACA,eACA,yCACA,WACAiL,oDAEAA,oCACA,CACA,GACAjL,uBACA,mBACA,sBACA,CACA,GACAA,kDACA,qBACA,qBACA,CACA,IAEAA,4BACA,qBACA,mBACA,CACA,GACAA,6BACA,mBACA,yCACA,CACA,GACAA,yBACA,qBACA,kBACA0+B,MAEAA,4BACAA,gCACA,CACA,GACA1+B,6BACA,gCACA,mBACA,YAAwB3J,KACxB2qB,MAD+B3qB,IAC/B2qB,QAGA,oCACA,CACA,GACAhhB,+CAGA,qBAEA,IADAghB,GACA,mCAEA,uCACA,WACA2d,gBACA1mC,kBAEA,CACA,IACA+H,mDAGA,mBAEA,IADAghB,GACA,mCAEA,uCACA,2BACAA,0BACA/oB,kBACA,CACA,IACA+H,4BACA,qBACA,kCACA/H,kBACA,CACA,GAEA+H,gDACA,qBACA,qBACA,CAAkB4+B,0BAClBr4C,uBAAwDq4C,aAExD,+BACA,SACA,kDACA,SACA19B,QACA8jB,eAEA,+BACA,kBACA9jB,SACAla,2BAEA63C,CAEA,CACA,IACA7+B,+CACA,qBAEA,yBAA2C4+B,kBAC3C,CACA,IACA5+B,4BACA,qBACA,qDAIA8+B,SACA7zB,KACAuG,2BALAvG,CAMA,CACA,GACAjL,iCACA,qBACA,uBACA,uCACA,kBACA,aACAyJ,SACA,oCAEA,MADA+H,yBAEA,CACA,CAEA,qDAIAstB,SACA7zB,KACAuG,2BALAvG,CAMA,CACA,GACAjL,8BACA,mBACA,4BACA,CACA,GACAA,4BACA,uBACA,YACA,CACA,GACAA,kCACA,uBACA,uCACA,gDACA,CACA,GACAA,2BASA,qBAEA,UACA,SAGA,oDACA,IAA8B++B,KAE9BC,cACA,gDACAr4C,KAEAA,CAEA,EAEAJ,8BAGA,QAFA04C,KAEA5oC,IAAgCA,WAAqBA,IACrD,iCACA4oC,KACA,KACA,CAGAA,GACAF,SAEA,GAEAtmC,GACA,CAEA,QACA,CACA,GAGAuH,0EAGA,qBAEA,SACA,2BACAk/B,8BAEA,cADAle,sBACA,GAEAme,yBACA,CACA,IACAn/B,4CACA,qBACA,iBACA,eACA,CACA,GACAA,gCACA,gFACAo/B,uCAEAC,gBACA,mBAEA,OADAC,oBAAkC,iBAClCrlC,OACA,YAEA,eACA,yBAGA,cACA,6BAEA,EAiBAslC,cACA,mBAEA,GADAD,oBAAkC,OAjBlC,YACA,0BACAvlC,OAEA,YACAA,OAEA,YACAA,OAGAA,MAEA,CAIkCylC,MAClC,YACA,YAEA,eACA,yBAGA,cACA,6BAEA,EAEAC,kBACA,eACArqC,EACA,KACA2E,QAEAA,WACA,EAKA,qBAEA,IADAinB,GACA,gCACA,mBACA,yCACA,IAQA0e,IARAC,kBACAC,kBACAC,+CACA,cACA,GAAShtC,0BACT,0BACA,GAuFA,OApFA,aACA6sC,6BACA,SACAzS,kBACA,SACAA,qCACA,UACAA,uBAGA,UACAA,kBAGA,SACAA,oCACA,UACAA,uBAGA,UACAA,qCAIA,aAEAyS,EADA,iBACA,4CAEA,QACA,2BAGA,4BAGA,QACA,SACAzS,qCACA,UACAA,uBAGA,UACAA,qBAIA,SACAA,oCACA,UACAA,uBAGA,UACAA,4CAIA,cACAyS,UACA,UACAA,wBAEA,UACAA,yBAEA,UACAA,wBAGA,UACA,QACAzS,kBAEA,QACAA,oBAEA,UACAA,6CAKA0S,YACAT,EACAQ,4BAEAA,SAIA,aACAA,UACA,UACAA,wBAEA,UACAA,yBAEA,UACAA,wBAEA,UACAA,uBAGAA,sBAEA,aACAA,UACA,UACAA,wBAEA,UACAA,yBAEA,UACAA,wBAEA,UACAA,uBAEA,UACAA,wBAGAA,mCAEA,aACAA,UACA,UACAA,wBAEA,UACAA,yBAEA,UACAA,wBAEA,UACAA,uBAEA,UACAA,wBAEA,UACAA,uBAGAA,2BArBA,CAwBA,CACA,GAEA1/B,4CACA,aACA,IAQAA,gEACA,kBAA0C,8BAC1C8/B,cACA,2BACA9e,6BACA,EAEA+e,GACA53C,SACA63C,aACAzmC,aACAiV,gBACA5V,qBAAoC,2BACpCxQ,kBAAiC,6BACjC67B,qBAAoC,mCACpC,kBACAhf,SAEAg7B,GACAC,gDAIA,yBACA,gCACA,cAA0B,SAC1BC,aAEA,kCACAC,QACAC,YACAC,WAEAF,SAEA,4BACA,wBACAE,mBAIA,KADAA,4BACArmC,SAAsCqmC,SACtCA,oBACA,gBACA,wBAKA,IAJA,sCACAC,mBACAC,kBAEA,OAEA,cAIA,IAFAt5C,EACAu5C,EADAR,KACAA,SACAS,UACA,UACA,uBACA,wBACA,EAAWH,EACX,CACA,CAEA,uBACA,wBACA,EAAOA,EAeP,GACA,wBAEA,gBACA,YAAsBlqC,WAA0BA,KAChD,gBACA,iBACA,CACA,QACA,EARAgqC,IASA,gBACA,SACA,cAA4B,eAC5BM,CACA,CAEA,cACA,iBACA,aACA,cACA,aACA,SACA,QACA,SAEA,CAEA,cACA,uBACA,8BAEAC,EADAzrC,gBAGA,+BAEAyrC,EADAzrC,gBACAA,EAEA,EACA,CAEA,kBACAzO,IACAm6C,OACAC,QAEA,eACAC,WAKA,OAJAC,sBACAH,OACAC,QAEAE,OACA,eACAH,kBACAC,mBAEAD,QACAA,UAEAG,QAEA,CACA,CACA,IACAhhC,2BACA,qBACA,iBACA,iBACA,CACA,GACAA,4BACA,mBACA,SACA,uCACAM,SACA,GACAA,CACA,CACA,GACAN,6BACA,qBACA,4BACA/F,UACA+mB,WACAA,oBAEAA,EACA,CACA,GAEAhhB,+BACA,kBACA,2BACA,qBACAO,oBAEAA,yBACAA,WACAA,mBAEAA,qCACAA,WACAA,wBAEAA,uBACAA,WACAA,KAEAnL,CACA,EAEA,mBACA,eACA,MACA6rC,eAQA,GANA1gC,EADA,iBACA,aACA,kBACA,aAEA0gC,UAEAA,eACA,0CAEA,sBACA,mBACA1gC,sBACA2gC,YACA3gC,oBAEAA,gCACA,CACA,GACAP,mCACA,kBAEA,iBADA,QACA,IACA,EAEA,mBACA,eACA,MACAihC,eAQA,GANA1gC,EADA,iBACA,aACA,kBACA,aAEA0gC,UAEAA,eACA,gDAEA,sBACA,mBACA1gC,sBACA2gC,YACA3gC,oBAEAA,gCACA,CACA,GACAP,sDACA,oBACA,uBACA,EACAmhC,iBAEA,mBACA,+BAEAppB,kBAEA,SAIAqpB,yBACA,wBACAC,oDACAtpB,aAEA,6CAEAspB,OADAtpB,qBACA,0BAEA,yFACAA,sBACAA,qBAEA,oBAEAspB,OADAtpB,qBACA,0BAEA,yDACAA,gBAEA,0BACAspB,6DACAtpB,qBAGAqpB,GACA,oBACAC,gEACAtpB,eAGA,wBACAspB,+EACAtpB,mBAGA,0BACAspB,wFACAtpB,qBAGA,sBACAspB,kEACAtpB,iBAGA,yBACAspB,+EACAtpB,6BAIAA,qBACAA,yBACAA,2BACAA,uBACAA,oBAGA,6CACAspB,6EACAtpB,sBACAA,qBAEA,oBACAspB,gEACAtpB,gBAEA,0BACAspB,6DACAtpB,qBAGA,2CACAspB,0EACAtpB,qBACAA,oBAEA,mBACAspB,8DACAtpB,eAEA,yBACAspB,2DACAtpB,oBAGA,oBACAriB,iCAGA2rC,OAFA,wBACAtpB,0BACA,+CACA,yBACA,0DAEA,2EAGA,gFAEAA,qBACAA,qBAKA,oBACAspB,6DACAtpB,eAGA,mDACAspB,oFACAtpB,yBACAA,wBAEA,uBACAspB,qEACAtpB,mBAEA,6BACAspB,iEACAtpB,wBAIA,oBAEAspB,OADA,uCACA,yCAEA,uCACA,wCAGA,4BAEAtpB,eAKA,qBACAspB,mDACAtpB,gBAEA,wBACAspB,2DACAtpB,mBAEA,qBACAspB,oDACAtpB,gBAEA,gBACAspB,yCACAtpB,WAGA,wBACAspB,0FACAtpB,mBAIA,uBACAspB,wEACAtpB,kBAGA,sBACAspB,wFACAtpB,iBAIA,wBACA,2BACAA,qBACAspB,+CACAtpB,qBAGAspB,gDAGAtpB,0BAGAA,cAEAxxB,gCACA86C,mBACA,GAEAA,YACA,CACA,IC96BA,WACA,aAEA96C,uCAEAA,yEAGA+6C,8CACA,6vFAmDAA,iDACA,s2MAoHAA,2CACA,mmKAuGAA,qCACA,2hGA4CAA,4CACA,u9MAsHAA,mDACA,+VAMAA,mDACA,sjDAiCAA,kDACA,4uBAaAA,gDACA,udAQAA,qDACA,mcAMAA,iDACA,0HAMAA,oDACA,ggBAMAA,qDACA,0IAOAA,kDACA,4vBASAA,sDACA,6eAOAA,oCACA,0pKA4EA,GACA,CA1nBA,GCCA,WACA,aAKA,8CAeA/mC,8EACA,OACAoM,OACAld,QACA83C,iBACAC,cACAC,eACAC,eACAC,YACAC,aAIAhtB,0EACAtI,qBAEA3F,4BAEAA,2BACA,KAEA,cAEAA,YAEA,0BACA5R,GAAgC8sC,8BAChC7hC,SACA4hC,oCACAH,eAGA,gBACA1sC,mBAA8CiL,wCAE9C,CAGA,qBACA,6BAGA,QAFA8hC,iBACAC,cACA1rC,IAAuCA,IAA0BA,KACjE,IACA2rC,QADAF,UAEAG,gEACAA,GACAltC,mBAA0DiL,yCAE1D,CAEA,CAEAkiC,6CAEAhX,wCACA,gBACA9tB,qCACA1H,6BACAA,6BACAysC,YACA,GACAD,4BACAC,iDACAx7B,+BACA,EAAiB,CAEjB,EAEA,EAEA,IAEAy7B,kIACA,OACA5gC,mDACAmF,SAIA2F,qBAIA3F,4EACA,0BACA/O,0CAEA,mBACA+O,+DAGA3f,kCACA2f,sBAEA,uBAEA07B,qBACAl4B,SAAoCm4B,gBAAsBpsC,iBAgB1D,GAfAyQ,QACA07B,YACA1nC,aACA0yB,0BAEA5jC,QACA,EACA,EAAyB,KAEzBkd,oBACAA,2CACAA,YACA,IAGAld,kBACA,MACA6tC,IACA7tC,sBACAiM,gBACAwV,gBAA8C5jB,WAAci7C,MAE5DC,gCACAA,eAGyBlzB,oBACzB5Z,+CAEyBmB,aACzBygC,IACA5hC,eACA4hC,sBACA5hC,mCACAiR,cACA67B,IACA9sC,2CACAiR,yBAGyB,EAEzB,CAGA,MADAA,kBAEAA,0BACAqmB,iDACA/S,UACAyP,sCACA/iB,6CACA,EAAiC,WAEjCqmB,IACAA,OACArmB,iBACA,EAEA,EACA,EACAA,iBACAA,+BACAqmB,IACAA,IACAA,QAEArmB,kCACAA,gBACA,EACA,GAEA,EAEA,EACAlF,iCACA,GAEA,IACA2gC,yCACA,OACA3gC,0CACAC,uBACA,GACAF,yDACA8K,qBACA,EAEA,GAEA/R,uDAEA,OACAkoC,aACA97B,OACA+7B,aAEA9tB,mIAEAtI,qBAEA,qCACAo2B,IACAA,UACA/7B,gBACAA,uBACAwD,6BACAxD,YACA,IAEAg8B,IACA,EACA,EAEA,IAEAP,yIACAj4B,SAKA,cACA,oEACA,CAsBA,OAGA,gBACA,uBACA,GAGA,gBAAgC,6BAEhC,kBACA,qDAeA,OAdA5H,qCACAqgC,kEAEA,OADAC,kBAEAA,YACAC,UACAvgC,gCAEAsgC,YACAC,UACAvgC,6BAEA,CACA,EAGA,CAkIA,oBACA,MACA,cAEA,SACA,gBACA,YAAgClM,sBAA2BA,KAC3D,+BACAomB,GACAsmB,SACA,CAEA,cACAC,iCACAC,qCACAA,SACAD,SACAzgC,0DAIAA,6DACAygC,mDAEAC,aACA,IAGAC,IAHAD,qCACAA,4BACAD,8EAEAA,WACA,CAGA,MAEAA,EAKA,oBAEA,uBACAG,eA9NA,cACA,eACA,WACA,mBACA,EACA,CAyNAC,UACAC,yBACA,cACAC,GACA/gC,gEACAghC,YAGA,IACAC,EADAC,oCAEAC,SACAnhC,qDAGAohC,KAEA,WAKA,yBAAoDV,QACpD,MACA18C,4BACA,mCACAq9C,8BACAlkC,wBACA8jC,KACAE,0BAEA,8BACAG,uBASA,GARAJ,YACAE,OACA,CAA0BjkC,MAC1B0K,WACAjV,SAEA2uC,QAEA,sCACA,yBACAC,aACA,CAEA,EAEA,MACAruC,sCAGA,CAkEA,IAhEAutC,kCACA,4BACAe,sBAEAv6C,iBACA2S,uBACA,2BAGA,uBAA+DwlC,oBAAwBl7C,uBACvF,oBACAk9C,0CACA34B,YAEAg4B,gCAEAh4B,GADA,kBACAg5B,WAEAA,eAIA,4BACA,WACAh5B,kFACAi5B,oBAEA,oCACA,GACA,aAGA,MACA,wBA9SA,cAGA,mDACAC,+CACA,6BACA,0CACAC,gBACAC,2CACA3nC,+BACA,CAoSA4nC,gBACA/hC,mDACAgiC,kBAEA,kCACAC,KACAC,6BACAnB,kCAGAmB,oDAGAf,2BACAD,aAGAE,QAAuCvmC,gBAA4B,EAEnE,EACA,GAEAomC,IACAP,eACAA,mCAzRA,oBACA,OACAyB,+BAEAxjC,UAEAxB,sBACA,mDACA5J,gCACAC,kCACA,CAAkC6d,kCAAwC1d,iBAC1E,0BACAiU,6BADA,IAEA,EAAiC,YAAqB,eACtDjU,iBACA,KAEA,KAEAyuC,EAFAC,yCACAC,yCAEAD,UACAC,SACAF,aAEAA,iCAEAA,kFACAzjC,cACAzX,sBACAk7C,iFACAl7C,yBACAk7C,gFAEA,EACA,EAEA,EACAG,wBACAplC,sBACA,4BACAqlC,oCAAuE5vC,OACvEnO,0CACA0V,iDACA,EAEA,EACAsoC,2BACAtlC,sBACAhD,gBACA6F,iEACA,EACA,EACA0iC,iCAWA,oCAEA,KACAC,eACAhkC,gBAFAxB,oBAEA,gBACAylC,yCAEAC,4CACAC,iDACAD,SAEA,eAEAlkC,OACAgiC,cAEAA,eAEA,iBAA2C7sC,WAAiBA,IAC5DqJ,qBAEA+jC,YAEA,EACA6B,gCAIA5lC,sBACA,MACA,eACA9X,qCAC0B,CAC1B,wCACA29C,IACA39C,2BApIA,cACA,0DACA,CAkIA49C,IAEA,CACA59C,GACA8U,sDAEA,EAEA,EACA+oC,4BAIA/vC,8BACAgK,sBACAhK,eACAgH,kCACA,EACA,GAGA,WACA,IACAgpC,wBAGA,CAFA,MAAc9+C,GACd8O,eACA,CAEA,CA4JAiwC,SACA,GAGAtC,aAEAG,EACA,QACA,CA/HAoC,UAGA,QACA,CA6HA,OAAsBrJ,mBACtBsJ,0BACAvlC,oBAEA,OACAmiC,aACA97B,OACAld,QACAi5C,YACA1U,cAIA1hB,qBAEA,MAEAw5B,EADA5D,eACAv7B,wBAEA3f,0CAGA,iCACA,KACA,4BAEA,uBAEA8+C,sBACA/B,4BAEA,GACA/8B,UAEArM,aACAunC,+BACAl7C,qCACA,mBACA2f,oBAA6C,+BAC7Cif,2BAEA,GACAoH,IACA,EACA,EAEA,IAEAzyB,uGACA,OACAkoC,aACA7tB,ysBAKAjO,OACAld,SAEA6iB,qBAEA,iCACA,wBACA,wBAGA,OAFAtF,cACAgmB,IAIArmB,kCACAA,gCAEAA,2BACAtH,0CACA,EAEAsH,yBACA,MACA,6CACA,2BACA,OAUA,OARAG,SACAH,cACAkN,WACA+R,wBACA/R,2BACA,iCACAhR,UAAgD0L,wBAChD,EAGA,IAAsBzH,wCACtB,kDACA,OAUA,OARAA,SACAH,cACAkN,WACA+R,wBACA/R,2BACAhR,UAAgD0L,8CAChD,EAIA,CACA,iCACAw3B,GACAljC,UAA4C0L,wBAI5C,EAEAye,GACA,EACA,EAEA,IAEAzyB,sCACA,OACA+R,uBAGA,IAGA05B,IAEAC,gBACA,MACA1jC,8BACA,uBAUA2jC,2CARAF,iCACAA,kBACAG,iCACAA,kBAEA,MAIA5jC,sFACA,CACAA,qBACA6jC,sBAtBA,GAsBA,MACAA,qBAtBA,GAsBA,MACAC,cACA,EAEA9jC,uBAEA0jC,OACA,EACA,WACAzwC,kBACA+M,6BACA,GAEA+jC,YAGA,EAGA,GAEA/rC,gDAGA,OACAkoC,aACA97B,OACAld,SAEA6iB,qBACA,kCACA7iB,IACAkR,aACAqM,kCACA,EAAa,KACbgmB,IACA,EACA,EAEA,IAIAzyB,8DACA,OACAoM,OACA6L,kBACA+zB,iBAGA3xB,o5BAWAtI,qBACA3F,cACAA,YACAA,WAEAK,2BAEAL,qBACAA,4CACAA,wBAEAA,WACA,EACAA,qBACAA,oBACA,CACA,EAEA,IAEApM,oDACA,uBACA,0BAEAo4B,EACAA,kBACA3rB,sBACc2rB,gBACd3rB,oBACc2rB,gBACd3rB,oBAEAA,cAGAA,sBAEA,CACA,IAEAzM,gEACA,OACAoM,OACA6L,kBACA+zB,iBAEA3xB,waAIAtI,qBACA3F,cAEAA,iCACAA,yBACAA,4BAEAA,WACAA,uBACA,+BACA,EACAA,yBACA,6BACA,KACAA,iBAEAA,oBAEA,CACA,EAEA,IACApM,4DAEA,OACAoM,OACA6L,kBACA+zB,iBAEA3xB,wSAIAtI,qBACA3F,cAEAK,4BACAL,8BACAjR,gBACAiR,oBACA,GAAO,GACPu7B,iCACAxsC,cACA,GACAiR,2CACAA,WAIAA,wBACAA,+BACA,CACA,EAEA,IACAy7B,kEACA,OACAoE,kBACA7/B,SACA2F,uBAGAtF,oDACAL,8BACAA,oBAAqC,gBAAkB,YACvD7D,sBACA,GACA6D,qBACAhM,aAAoCqM,YACpCL,WACAK,SACA,EACAA,oBAZA,2IAYAy/B,IACA,EAEA,IACArE,qJACA,OACAz7B,SACA87B,aACA7tB,gEACAtI,qBAIA3F,OACA2oB,yDACAoX,kBACAC,iDAEAhgC,mBACAigC,2DACAC,4DAGAlgC,wBACAA,wBAEAA,cADA,wBACA,YAEA,SAGAlL,yCACAzF,4DACA2Q,gBACAA,8BACA,EAEAA,+DACAA,0BACA,yEACA,EACAA,+BACA3Q,wCACAA,aAAyC8wC,uBACzCngC,2CACAA,gCAIAA,6BACA3Q,aAA6C8wC,uBAC7CngC,2CACAA,+BAIA,GAKAogC,8CACA,GAEApgC,iCACAhM,aACAgM,gEACA,EAAiB,EACjB,GAEAA,mCAAqD4M,OACrD5M,iCACA8E,OACA9E,yDAEA,GAGA,mCACAA,6BACAA,wBAEAqgC,SAEA,2BACArgC,oBAEAA,gBAEA,CACA,EAGA,IACAy7B,mDACA,OAEA91B,qBAIA3F,oBACA,yBACA,EACAA,aACAA,wBACAA,mDAIAA,qEACA,MAIA,QAHAsgC,sCACAC,KACAC,IACAF,UACAC,gBACAD,+BAEAtgC,aACAK,sBACAkE,gBACA,CAAoC9N,+BACpCgqC,yCACAC,mCACAn/B,OAEAoH,0BACA3I,aACAK,sBACAzgB,qBACAogB,mBACAA,8BACAA,0BAAmE+J,4CAInE/J,oCAA2E,gCAC3EA,gCACA,EAA6B,YAC7BA,aACAK,sBACAtR,eACA,EACA,MACAiR,oCAIA,GAGA,EAEA,IACAy7B,yCACA,OACA5gC,mEACA8K,qBAGA3F,aAAgCuB,gBAChCvB,qBAAwCA,qCAExCA,uBAEA,6BADArC,kDACA,mBAAuDA,OACvD,CAEA,EAEA,GACA89B,6EAIA,OACAz7B,OACA6L,qBACA80B,eACAC,qBACAC,eACAC,qBACAC,qBACAC,uBAEAC,qBACAC,gBACAC,iBAEAC,cACAvmC,+DAEA8K,qBACA3F,wBACAA,0BACAA,0BACAA,0BACAA,4BACAA,wCACAA,8CAEAA,4BAEAA,4CACAA,aACAA,WAEAA,sBAGA,4BACAA,sBAA2CqhC,YACjC9F,mBACV+B,uCAMApyB,gBACA,EACAlL,wBAEA,8CACAA,oDACA,EACAA,wBAEA,yBACA,UAEAhgB,CAEA,EACAggB,qBACA,yBACA,4CAAgEqhC,6BAAgC,OAEhGrhC,gDACAA,oBACA,MAAUA,+CACVA,yBACAA,aAEA,EACAA,qBACAA,oBACA,EACAA,wBACA,oCACA,EACAA,sBACAu7B,mBACAv7B,qBAEAA,YAEA,CAEA,EAGA,IAEAy7B,0CACA,OAGAK,aACAn2B,qBACA,uCACA/lB,6CACA,yCACA,2BACA,CAEA,EACA,EAEA,GAEA67C,qDAGA,OACAz7B,OACA6rB,UACAzqB,SACAkgC,cAEArzB,sSAKAtI,qBACA3F,6BACA,MAEA,+BACAA,aAEA,yBACA,4BACA,2BACA2G,qBAA8C,aAC9C3G,eAEAA,yBACAA,YAAwC9E,QACxC,CACA,CACA,EACA,EAEA,IAEAtH,yFACA,OACAoM,OACAsN,iCACAi0B,YACAD,aACAnhB,oBAEAtlB,yDACA8K,qBAEA,gDACA3F,YACAK,qCACAL,YAAoC+T,SACpC,EAEA,EACAjZ,iCACAC,uBACAA,2BACA,SACAymC,WACAA,aACAzmC,wBAEAymC,WACAA,aACAA,eAGAA,aACAzmC,yBAGA,qBACAymC,WACAA,aACAzmC,wBAEAymC,WACAA,aACAA,eAGAA,aACAzmC,wBAIA,EAEAA,oCACA,mBACAlZ,gCACAkZ,mCACAoB,UACAslC,gBAEAtlC,WACAulC,eAEA,GAEAD,UACAlgC,8CAEAmgC,UACAngC,qDAEA,gBAEAogC,2BACAC,iBACAhiB,cACA1jB,8BAA+DyP,YAC/D,EAEA,kBACA5Q,0BACAnb,oCACAA,gBAEAmb,mCACA8mC,uBACAA,4CACA,mDACAA,6CACA,0DACAA,4CACA9mC,0CAEA,SACA+mC,mBAGAD,MADAA,UACA5iB,0BACA8iB,iCAEA9iB,0BACA2iB,mBAEA,GACA7mC,uBACA,gCACAA,0BACAA,uDAEA,EAGAkkB,8BACAA,iDACA+iB,sBACA,EACA,GAGAjnC,4CACAiyB,OACAA,MACAgV,IAEA,GAEA,gCACAjnC,eACAknC,cACAC,wBACAC,aACAC,aACAC,WACAC,uBACAC,qBAEA,0BACAC,4CACAC,gCAEA5zC,+BACAA,qCACAA,kDACA,GAGAkM,6BACA2nC,EACA3nC,6CACAA,6CAEAA,6CACAA,8CAEAA,gDACAA,yBACA,EAEAA,oBAA0C,+DAC1C,YAEAA,8CACAA,6CACAA,0BAEAA,gDACAA,6CACAA,yBAEA,GAEAA,mCACA,+BACAoF,0BACAA,MACA,EACApF,oCACA,8DACA,+BACAoF,0BACAA,sDACA,yCACAA,MACA,GAGA6hC,sBACA,GAEA,IAaApuC,+IAEA,OACAoM,OACA2iC,YACAC,YACAC,gBACAC,gBACAC,YAEAloC,oDAEA8K,qBAEA,uBACAq9B,mCAEA3iC,wCACAA,4BACAk7B,iCACA0H,oBACAD,mBACA,EAAa,YACbA,kBACA,GAEA,kBACA,uBACA,gBACAE,+CACAtnC,0BACAqnC,WACA,MACAA,2BAEAA,+BACAD,kBAEA,EA2EA3c,iCACAvjC,IACAlD,kCACAuP,4BA5EA,YACA,gBACAyM,2BAEA9Y,mDACA,cACAqgD,uBACA7kC,aAEAA,GADAA,iBACArJ,2BAEA+K,UACAA,oBACAA,mCAA0D,kCAA+Cyc,UACzGzc,2DAAyE,IACzE,2BAAwDrf,cAAkBgoB,gBAO1E,GANAxZ,2BACA6Q,aAAoCojC,gBACpCpjC,2BACAA,+DACA,GAEAu7B,oDACA,iBACAr/B,4BAAyD0L,oBACzD1L,eAA6C8B,aAC7CilC,gBACA,MACAA,sBAGAI,6BAEA9H,yCACA0H,mBACA,wCAAiEjkC,aAAiBlc,OAClF,CACA,GAEAogD,oBAEA,sBAIAD,eAGA1H,sDAAsF16C,KAKtFwf,SAJA,+DACAkH,mBACA,wEACAA,+BAVA+7B,IAaA,EACAJ,qBACAr0C,iBACAy0C,IACA,EACA,uBACAxjB,EACAojB,QAEAI,IAGA,EAAa,YAEbA,IACA,EACA,CAMAC,GACA,GACA,sBAEA,EAGA,EAEA,IAEA3vC,uHACA,OACAoM,OACAhC,WAEAnD,6DACA8K,qBACA3F,WACAA,SACAA,WAEA,qBAEAA,iCACAhP,GACA3B,0CACAgR,0CACA,EAEA,GACAL,qBAEA,2CACA6mB,GACAA,mBACA/jC,gCACAA,mBAAmCy4B,kBACnCz4B,mCAA2D0gD,uBAC3D,GAEAxjC,aACAA,cACA,IACA,CACA,CACA,EAEA,IAEApM,yDACA,OACAqa,4KAGAtI,qBACA41B,gDACA,uCACAv7B,4BACA/e,OACA+e,yCAAgE,0BAEhE,EACA,EAEA,IAEApM,+CAWA,OAIA+R,qBACA,IAEA89B,sBAEAA,qBAEApjC,mCADAojC,2CACA,kDAEApjC,uEAGAA,mBACA,0BAEAk7B,uBACAkI,sBAEAzvC,aACAyvC,mBACA,EAAS,GAET,gDACAC,4CACAC,sCAEAtjC,oJACAA,kHAEA,EACA,EAGA,IAEAzM,uCAOA,OACAoM,OACA4jC,mBAEA31B,mJAEAtI,qBACA3F,gBACAA,oBACAA,UACAA,wBACAA,eACAA,8BACAA,uCACAA,iBAA6B0C,aAC7B,CACA,EAEA,GAEA9O,qCAEA,uBACAytC,sCACA,eACArhC,oBACAA,sBACA,GAEA+T,mBAEA,EACA,CACA,GAEAngB,kCAMA,OACA+R,qBACA41B,iCACA,uBACA,yBACAl7B,SACA,MACAA,mCAEA,EAEA,EAEA,GAEAzM,iDAEA,OACAkoC,aACAn2B,qBACA,6BACA2W,SACA,QACAtoB,aACAqM,oDAAiEswB,uCACjEtwB,qBACA,EACA,EAEA,IAEAzM,uCACA,OACAisC,kBACAl6B,uBAIAtF,kBACAimB,uBACAud,mBACA,EACA,EAEA,GAEAjwC,sCACA,uBACAyM,uBACA,eACA,sBACApD,gDAGAoD,kBAEA,CAEA,EACA,CACA,GAEAzM,iCAEA,OACAisC,iBACAl6B,qBAKA3F,oBAEA,yBACA,EAAO,YACPhP,EACAqP,mBAEAA,qBAEA,EACA,EAEA,GAgBAzM,kDACA,OACAkoC,aACAn2B,qBACAm+B,UACA/oC,8BACAgpC,GACA/vC,aACAgwC,YACA,EAAyB,EAEzB,GAEAF,UACA/oC,8BACAgpC,GACA/vC,aACAgwC,YACA,EAAyB,EAEzB,EAGA,EAEA,IAEApwC,sDAIA,uBAGA2nC,wCAEAvnC,aACAqM,0BACAa,mBAAgC,iFAChC+iC,mBAAuCroC,mBACvC,EAAW,GACX,EAEA,CACA,IAEAhI,qDAEA,OACAkoC,aACAn2B,qBAGA3R,aAKAkwC,uBACAC,WAAyBC,4EACzBC,YACA/d,0BAIA1qB,iDACAA,iEACA,EACA,GAEA,sCAEAoE,qCACAskC,iCACA,GACAtkC,uCAGAskC,mCACA,EACA,EAAO,EACP,EAEA,IAEA1wC,qCAOA,OACAoM,OACArH,wBAEAgN,qBAEA,iBACAtF,gCACAL,+BAEArH,EACAyY,wBACAA,sBAGAxV,uIAGAwV,yBACAA,+CAEA,EACA,EAEA,GAEAxd,uDAGA,yBACAI,aAEA,MACAiJ,uBACAhd,sCACAskD,oCACAlkC,yBAEApgB,gCACAukD,6BACAnkC,mCAEA,EAAW,GACX,CACA,IAEAzM,2CACA,OACAkoC,aACA7tB,k+BAYAtI,qBACA41B,yCACA,gBACAv7B,oBACAA,iFAGAA,qBACAA,4CAEA,EACA,EAEA,GAEApM,kEACA,OACAqa,sQAKAjO,OACAykC,WAEA9+B,qBACA3F,8BACAhC,GACA5O,0DACA,IACA8Z,yBACAw7B,WAFAz7B,6BAGA07B,cAEAA,4BACA3kC,qBAEAA,sBACA+f,wCACAhxB,gBACA41C,4BACA3kC,oBAEA,EACA,CACA,EAEA,EACA,EAEA,IAEApM,kHACA,qBA2BA,cACA,IACAgxC,EADAvkC,qBACAwkC,2BACAD,SAIAA,kBAHA71C,qCAIA,CAjCAiR,kDAEAA,uBACA3f,0DACA,EAEA2f,4BACAA,aACA8kC,MAAwBC,oDACxBC,SACA3iD,WACAuN,YACAq1C,UACAC,WACA7pC,QAAyB8pC,6BACzBC,aAEA,EACAplC,mBACAlL,6BACAkL,kBACA,GAcAA,4BACAqlC,YAIArxC,aACAsxC,IACA,EAAa,IALbv2C,qDAMA,EACAiR,4BACA,gBAEAK,OADA,aACA,uBACArP,OACA,EAEAgP,wBACAqlC,eACA1kD,kBACAub,sBACAA,gCACAA,4BACAlI,aACAsxC,IACA,EAAqB,GAGrB,EAEA,OACA11C,UACA0O,kBACA6gC,yDAEA98C,SACAic,iBACA6gC,wDAEAoG,cACAjnC,uBACA6gC,6DAEAqG,WACAlnC,2BACA6gC,0DAEAsG,WACAnnC,6BACA6gC,0DAEAuG,YACApnC,2BACA6gC,2DAEAwG,eACArnC,kCACA6gC,8DAEAyG,cACAtnC,qBACA6gC,6DAEA0G,gBACAvnC,wBACA6gC,6DAGAv/C,gCACA+1B,MACA,GACA3V,UAEAA,8CAEA,MAEA8lC,EACAC,KAEAC,gCACAC,oDACAD,2BACAF,eAGAE,gCACAC,4BAEAD,oCACAC,6BAEAD,8BACAC,gCAEAD,+CACAC,sDACAH,oBAEAE,yCACAC,yCACAH,kBAEAE,8BACAC,qCACAH,iDAGAG,qCACAF,MAGAA,IACA/lC,sBAEAA,2BACA,mBACAqlC,OAEAS,oBAIA9xC,aAEAgM,eACA,EAEA,EAEA,CACA,IAEApM,2DACA,OACAkoC,aACAjhC,2EACAmF,OACAqnB,aACA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CAA,qDACA,4BACA,GAEAA,wBACA,wCACA,mBACAK,qCACA,gBACA0f,UACAvmB,wBACAC,0CACiBE,wBACjBqG,kCACA/Q,OAA2CwJ,kDAC3CuH,oBACA,EAAqB,YACrBK,kEACApR,yCACA,EACA,EAAiB,CACjB,CACA,EAEA,IACAwsC,4DACA,OACAK,aACAjhC,oEACAmF,SACA2F,qBAEA3F,OAA0B1N,sBAG1B0N,0BACA,aACA,CAAsB8hB,eAAe9hC,SAAU,yBAG/CqP,MACA,CAA0ByyB,eAAe9hC,SAAU,yBAEnDqP,0CAGAA,eAA6CyyB,eAAgB9hC,gBAAgB0P,aAC7E,EAEAsQ,iCACAA,8BACAA,kBACAA,wCACAA,iCAEAA,mCAEA,EAEAA,yBACAhgB,yBACAggB,kBACAA,qBACA,CACA,EAEA,IACApM,sDAIA,cAAiC,4BAAqB,EACtD,uBACA,qBAEA,cACA0gC,GACA9iC,6CACAA,mBAEAA,0CACAA,WAEA,CACA+pC,uBACAA,6BACA,kBACAA,oCACAqN,WACA,EAGA,CACA,IACAh1C,uEACA,OACAkoC,aACA97B,OACAld,SAEA+X,gEACA8K,qBACA3F,4BACAld,GACAuM,uCACA2Q,kBAAsD8hB,eAAgB9hC,YACtEggB,iBAAqD8hB,eAAgBh/B,eAAe,qCACpF,EACA,EACA,EACAgY,yGACAC,SACAA,WACAA,YACAA,aACAA,aACA8tC,kBACAC,wBACAC,8BACAC,kBACAvqC,qBAEA1D,uBACA2Y,4DACA,EACA3Y,sBACAglB,oCACAvc,uBACAtH,UACA,EACA,EACAnB,qBAAyC,4CACzCA,uBACAglB,uBACA,EACAhlB,qBAAwCvG,eACxC,GAEA,IAEAZ,sCAuCA,OACAisC,kBACAl6B,uBACA4a,oCACA,uBA1CA,cASA,GARA,eAIA0oB,uBAIA,gBACA,SAKA,QAFAC,eACAC,IACAz5C,IAAsBA,KAAQA,KAC9B,0BACA,OACAy5C,SACc,CACd,UACAC,MACAA,MAEAD,IACA,CACA,CACA,WAIA,OAHA,OACAE,QAEAA,IAKA,CASAC,6BACA,CACA,EAEA,GAEA11C,sCAGA,OACAkoC,aACA+D,kBACAl6B,uBAEA,gCAEA8L,0DACAzR,oBACA,2CACA,EAAa,cACb,aACAyR,0DACAre,MAEA,SASA,aARAqe,qBACA,iBACA5F,KACAA,sCAEAA,iCAEA,GACAA,EACAA,yBACAg4B,uCACA,EAEA,CAEA,GACAA,4BACA,SAGA0F,KACAC,KACA,4BACA,kBACA,iCAEA3F,gDACA2F,UAEAC,mBACAF,UACA/T,KAEA,GACAgU,sBACAE,2BACA,GACAH,sBACAG,2BACA,GACA7F,2BAEA4F,CACA,EAEA,EAEA,GAEA71C,4CACA,OACAkoC,aACA97B,SAEAiO,uIAGAhZ,WACA0Q,qBAEA,mBACAgkC,4BAEA3pC,oBACA,gBAGAA,MADAu7B,WACAv7B,oCAEAA,kBACAA,iBACAoO,kCAEApO,eACAkkC,sBAEA0F,eACA5pC,6BAEA4pC,kBACA5pC,0BAEA,EACAA,SAGAA,kBAEA,QADA7P,eACAT,IAA8BA,WAAiBA,IAC/C,iBAMA,OALAsQ,QACAA,sBACAA,eACAkkC,sBAKAlkC,SAEAA,MADAu7B,WACAv7B,oCAEAA,kBACAA,iBACAA,eACAkkC,oBAEA,EAEA,oDACAlzC,QACAgP,SACAqmB,IAEA,EACA,EAEA,IAEAzyB,6CAEA,IAGAi2C,kBAEA,8BAEAC,OACAC,IAEAA,MAEAnuC,aACAouC,OAGA,EAEA,OACAlO,eACAn2B,qBAEA/lB,4CACA,mCAEAiqD,MADAI,mBAEA,GAEArqD,2CACAsqD,sBACA,mCAEAL,MADAI,mBAEA,EAAW,EACX,EAEA,EAGA,IAEAr2C,iCACA,uBACAytC,sCACA,eACArhC,oBACAA,kBACA,GAEA+T,mBAEA,EACA,CACA,GAEAngB,4CACA,uBACAyM,mBACA,kCAEA8pC,EADAC,8BACAC,sFACA,qBACA,sCACAF,YACA,CACA,2JACAxU,2BACA2U,2CACA3U,4FACAsG,4FACAA,4DACAtG,yBACA54B,UACAA,SACA,EAEA,CACA,GAQAnJ,yDACA,uBACA,SACA,kCACA22C,IACArG,sBAEAA,wBAGA,iBACA,IAKAhpC,EALAsvC,eACAC,eACAC,0BAIAC,GAHAF,qBAGA7uC,cAGAV,EAFAyvC,oBACA/uC,qBACA8uC,IAIAl2C,uBAIA0vC,WACA,EAEA/zC,KAOA6P,oBANA,gBACA7P,2BACA6D,QACA7D,KAEA,GACA,GAEAyL,yBAEAoE,4BACApE,yBACA,EACA,CACA,IAGAhI,2DACA,uBACA,QACAg3C,KACArP,sBACAsP,uBAEAtP,wBACAqP,oCAEA,iBACA,UACAJ,eACA5uC,aAEAkvC,2BAGAF,sBACA,WACAvqC,WAEAyqC,cAEA,GAEAt2C,OAEA0vC,8BACA,EAEA/zC,KAOAk2B,sBANA,gBACAl2B,2BACA6D,QACA7D,KAEA,GACA,GAEAyL,yBAEAoE,4BACAqmB,IACAzqB,yBACA,GAEA2/B,cACAv7B,qBAEA,CACA,IAQApM,mDAUA,OACAisC,gCACAl6B,uBACA,IAEAolC,EAFAC,KACAC,IAEAC,oCAiBA,aAEA,yCAGA5uB,mBApCA,aACA,wCACA5I,eAEAzW,gGACAkuC,SAEA,CA6BAC,IACAC,mCACAA,6BACAN,KACsBM,mCACtBA,+BAEA,CA3BA9P,gBAEkB,mCAElB,iCACAyP,KACAC,0CACsB,kCACtBD,KACAC,0CARAD,KAYAD,qCAgBAG,iBACAI,IAGAtrC,4BACAkrC,iBACA,EACA,EAEA,IAAKzP,4CACL,OACA3gC,mCACAjM,eACA,GAEA,IAGA+E,mCACA,OACAkoC,aACAn2B,qBACA,gBACA,oCACAu+B,cACA,MAEAA,aAEA,EAEA,GAEAtwC,6CACA,OACAkoC,aACAyP,sBACA,4BACA,qBACAlK,2BACA,gDAIArhC,SAHA,WACAwrC,OACA,EAGA,EACA,CACA,EAEA,IAGA53C,oDACA,OACAkoC,aACA97B,SACAiO,4EACAtI,qBACA3F,oDACA,gBACAA,mBACAyrC,QACAj6B,YACAlT,oBACAotC,cACAz9B,mXAKiB1e,iBACjB,SACA3P,oDACA+rD,GACAr4B,qBACA,GACA0T,QACA4kB,iBACAC,eAEA7kB,SACA4kB,gBACAE,GAEA,EACA,CACA,EAEA,IAEAl4C,uCACA,OACAkoC,aACA97B,OACAqT,8BAEAxY,mEACA8K,qBACA/lB,yCACA,mBACAwxB,sBAEA,EACA,EAEA,GAEAxd,6CACA,OACAkoC,aACA97B,OACAqT,+BAEA1N,qBACA3F,SACA,CAAiBuS,mCACjB,CAAiBA,4CACjB,CAAiBA,kDACjB,CAAiBA,8CAEjBvS,0BACAA,mBAAuCvH,iDACvC,CACA,EACAoC,0EAEA,GAEAjH,uCACA,OACAkoC,aACA97B,OACA+rC,oBACAC,iCACAxE,wBAEA3sC,mEACA8K,qBACA3F,oBACAA,kCACAA,oBACA,SACA2rC,mBACA3rC,gBACA,CAEA,EAEA,GAEApM,sCACA,OACAkoC,aACA97B,OACAisC,gBAEApxC,kEACA8K,qBACA3F,oBACAA,gBACA,CACA,EAEA,GAEApM,EACA6nC,iDAEA,OACAyQ,cAA2BxrC,eAC3ByrC,8BACAC,WAAwB1rC,eACxByrC,4BACAE,YAAyB3rC,eACzByrC,0BACAG,WAAwB5rC,eACxB6rC,2CACAJ,yBACAK,UAAuB9rC,gBACvB6rC,uDACAJ,wBACAM,eAA4B/rC,eAC5ByrC,gCACAO,gBAA6BhsC,gBAC7ByrC,+BACAQ,gBAA6BjsC,gBAC7ByrC,sDACAS,cAA2BlsC,gBAC3ByrC,2BACAI,wFACAM,sBAAmCnsC,gBACnCyrC,+BACAI,4DACAO,oBAAiCpsC,gBACjCyrC,4BACAI,2CACAQ,aACAZ,qCACAI,yMAIAS,mBACAb,4BACAc,WACAd,gDAGA,OACArQ,aACA97B,OACA9Q,qBAEAyW,qBAEA,iBAEA,mCAIA,aAHAtF,oCACAA,cAEAL,YACAA,oBAEA,0BACA,oCACAm/B,uBAEAA,+CACA+N,cACA/N,WAEA9+B,YAEA,EACA,IAGAxgB,gDA+BA,mBACAgP,wCAAoDwK,mBAA0B,WAAatZ,IAC3F,YAAoD,OAhCpD,cACA,qBAEA,OADAk6B,6BACApN,WACApJ,YAEA,mBACA,WAEA,oCACA,IACA0pC,sBACA,mBACAvY,YACA,IACA,WACAwY,aACAze,iBAIA,CAHA,MAAsB1uC,GAEtB,YADA8O,eAEA,CACA,GACA4/B,EACA,CACA,CAEA,kBAA+B,SAE/B,CAGoD0e,MAEpDx+C,wBAIA,OAHAA,qCACA,aACA,EAAqB,GAErB,CAEA,CACA,IACA4sC,oDASA,OACAz7B,OACAm/B,kBACAn/B,WAEA2F,qBAEA,MACA3F,4BACAstC,WACAttC,kCACA,GACAA,6BACAK,mBACA,EACA,EAEA,IAEAo7B,4EAKA,OACA91B,qBACA,IACAgE,EADAlG,gBAEA,cACAkG,aAAqC3J,6BACvBu7B,UACd5xB,aAAqC3J,4BACvBu7B,UACd,0BACAgS,OACAjsD,yCACAooC,wBACA/f,aAAqC,iBACrC4xB,SACA7nB,+BAEAxX,WACA,CACA,MACAyN,aAAqCtJ,cAErC,sCAEA,oBAUA,QANAmtC,EACAC,EACAC,wCAA6E,QAE7Exf,oBACAyf,kBACAj+C,IAAgCA,WAAuBA,IACvD,qBACA,gCACA,MACA89C,OACAC,OACA,KACA,CACA,CAMA,MACA,8BAGA7xC,uEACA6xC,sCACA,CAEA,CAEAztC,4BACAuJ,6BACA,EACA,EAEA,IAEAkyB,8HACA,OACAz7B,OACA2iC,aAGA9nC,gEAEA8K,qBAIA,GAHA3F,WACAA,SAEA3f,YAGA,sNACAutD,mEAEA5tC,2BACA,iBACA5M,qCACAw6C,kCACA,EAEA5tC,0BACA6tC,gCACA5+C,OACAC,sBACAuJ,gBAEA,EACA,EACAuH,yBACA6tC,+BACA5+C,OACAC,qDACAuJ,gBAEA,EACA,EACAuH,+BAEAA,qBACArF,QACAC,kBACAM,iBACA1G,eACAqG,+DACAC,4DACAC,gDACAA,SACA,GACAmN,SACA5J,iBACA,sBACA,EACAI,eACA,0BACA,IAGA,EACAsB,sBACAA,qCACA/Q,OACAC,iFACAuJ,gBAQA,EACA,EACAuH,uBAGAA,sCACA,8DACA3Q,UACAH,kDAGAD,OACAC,IAHAA,6EAIAuJ,gBAEA,EACA,EAEAuH,cACA8tC,4CACAltC,4CACAmtC,6CAGA,qCAEA,kDACA,8BACA7f,qCACA8f,KAWA,GAVAlrD,yCAIA,IAHA,yEACAkrD,wDAEAC,GACA,+BACA5wC,WACA,CACA,GAEA4wC,IACAjuC,qBAEAA,4BACAA,sBACA3Q,wCACA2Q,kBACA,EACA,GACAK,wBAEAL,qBACA,uBACA,gCACA,aACA,yIAEAkuC,kBACAluC,oBACA,GACAmuC,eACAnuC,4BACAmuC,UACA,EACA,MACAnuC,qBAGA,MACAA,sBAKAqmB,GACA,CACA,EAAa,CACb,EAEA,IAEAoV,wEACA,OACAz7B,SACAnF,sEACA8K,qBAGA3F,WACAA,uCACArf,GACAqf,gBACAuE,cAAsC5jB,MAAO,YAC7Cqf,wCACA,IAEAA,kBAEA,EACA,EAEA,IAEAy7B,2CACA,OACAz7B,OACAouC,cACAC,gBACAC,iBAEAzzC,4DAEA8K,qBACA,2CACA3F,iBACAA,gBAEAA,mCACAA,sBACA,GACAA,iCACAsD,IACAtD,wBACAsD,sBACAhF,yCACAA,yCAEA,GAEA,EAEA,EACAxD,iCACAC,2BACA,sDACA,CACA,GAEA,GACA0gC,gDACA,OACAz7B,OACAuuC,qBAEA1zC,iEAEA8K,qBACA3F,uCACApB,kCACA,EACA,EACA9D,iCAEA,GAEA,GACA2gC,4GACA,OACAz7B,OACAwuC,kCAEAvgC,uFAEAtI,qBACA3F,oDACAA,SACA1B,cACAmwC,QACA5tD,mBACA6tD,2BAEA,EACA,EACA5zC,+CACAC,wBAEA,WACAla,gCACA8tD,WACAvE,UACAn8B,6MAEAhT,6CACAC,UACAJ,uDACA6zC,kBACA5zC,iBACA,EACA,KAEAqvC,2BACA,IACAwE,EADAhzC,+BACA,iBACA+yC,mBAGA,uCAA+D,IAE/D,IACA,oBACAC,yBACAhzC,yCACA,+DACA,0BACA,CACA,CAA6CnF,0DAC7C,CAA6CA,0DAG7C8W,SAAyEzI,MACzElJ,yBACAwuC,UACAluC,2CACA,EACA,CACA,GACA0yC,kBAIA,CAHA,MAA0B3uD,GAC1B8O,gBACAq7C,SACA,CAEA,EAAqB,YACrBr7C,gBACAq7C,SACA,EACA,EACA,CACA,GAEA,IACA3O,2CACA,OACAz7B,OACAuuC,qBAEAtgC,kYAMAtI,qBACA3F,uCACAA,4CACA,EACA,EAEA,GACAy7B,wCACA,OACAz7B,OACAuuC,qBAEAtgC,gaAIAtI,qBACA,6IAEA3F,uCACA,gBACA,sBACA,aACAA,2BACsBqN,WACtBrN,uBACsB,CACtB,QACA6uC,wBACA9zB,+CACA+zB,MACAC,IACAA,QAEA/uC,2BACA,EACA,CAEA,CACApB,IACAoB,6CACA,EACA,EAEA,GAEAy7B,gDACA,OACAK,aACAn2B,qBACA07B,2BACA2N,+CACA,EACA,EAEA,IAEAvT,mCACA,OACAz7B,OAAiBivC,iBACjBtpC,qBACA3F,mCACA,MACA,iCACAoR,SACAA,iBAEA/Q,mDAEA,CACA,EACA,EAEA,GAEAo7B,gGAGA,OACA5gC,wEACAmF,OAAiBld,SACjB6iB,qBACA3F,2BAEA,MAeA,uCACA3G,mBACA,0DACA,GACA2G,YACAkvC,aACAC,aACA9jC,eACA+jC,aAEA,qBACAC,QAEArvC,2BACAsnB,mBACAA,oBACAtnB,wCACA,EACAA,+BACA,8BACAqvC,sBACAjsB,GACArD,gBACA,GACA/f,sBACA,EAEA3Q,wCAEAigD,sBACA9xB,2DACAjlB,wBACAlY,6CACA2f,6BAEAA,4BAEA,GACAA,wCAAkE,gFAClEA,wCAAkE,+EAElE,EAxDA,CACA,cAMA,gEAA8F1e,OAL9F,CACAiuD,aACAC,QACAC,iBAE6GlgD,KAC7G,YACA,qBACA0B,wDADA,IAEA,GAAyBhB,kBACzB,WACA,EACA,CA2CA,EAEA,EAEA,IAEAwrC,oEACA,OACAK,aACA7tB,sDACAtI,qBACA3F,+BACA,EAEA,IAEAy7B,qGACA,OACAz7B,OACA0vC,cAEA70C,kEACA8K,qBACA3F,WAEA,qNACA4tC,mEAEA5tC,2BACA,iBACA5M,qCACAw6C,kCACA,EAEA5tC,0BACA3Q,0CAEA,EACA,EAEA,sCACA,+CACA,8BAEA,OACA,2BACAvM,yCACA,+BACAua,WACA,EACA,CAEA4wC,IACAjuC,mBACAK,yBAGAgmB,GACA,CACA,EACA,EAEA,IAKAoV,sFACA,OACAz7B,OACAxB,UAEA3D,0BACA,gFACA,EACA8K,qBACA3F,QACAA,kBACAA,sBACAA,SAEAA,2BACA2vC,qCACA3vC,kBACAA,UACA,EACA,EACAA,kBAEA2vC,yCACA,8CACAC,sBACA5vC,eACAA,gBAEAqmB,IAEA,GAEArmB,sBACAA,eACAA,qCACA3Q,OAA0C2J,WAAWwnB,yBACrDlN,UACAq8B,2CACA3vC,iBACA,EAEA,EACA,EACA,EAEA,IAEAy7B,wFACA,OACAz7B,OACAxB,UAEA3D,0BACA,kFACA,EACA8K,qBACA3F,QACAA,oBACAA,SAEA2vC,uCACA3vC,oBACAA,YACA,GACAA,kBAEAA,gBAEAA,qBACA,4BACAA,iBAEA,EAEAA,2BACA2vC,mDACA3vC,sBACAA,eACA,EAAiB,YAEjB,EACA,CACA,EAEA,IAEAy7B,mFACA,OACAz7B,SACAiO,+FACAnT,iCACAC,oBAEAjG,yBAEA4e,mBAEA,CACA,GACA/N,qBAKA,EAEA,IACA81B,6BACA,OACAz7B,OACA6vC,WAEAlqC,qBACA3F,6BACAK,4BACA,EACA,EAEA,GACAo7B,mCACA,OACAK,aACAjhC,iEACAmF,OACAoO,UACAmqB,cAEA5yB,qBACA3F,YACA,CAAiBrf,iBACjB,CAAiBA,iBACjB,CAAiBA,iBACjB,CAAiBA,iBACjB,CAAiBA,iBACjB,CAAiBA,iBACjB,CAAiBA,kBAEjBqf,QACA8vC,wBACAC,YACAC,iCAEAhwC,6BAEAA,oBAEA,QADA,iBACA,eAEA,EAEAA,+BACA,iBACAA,sBAIA,MACA,mBACAg5B,kBACAC,kBACAj5B,0BACA,YAAkCtQ,WAAcA,IAChDsQ,uBAEA,QAA+BtQ,WAAcA,IAC7CsQ,gCAEA,mCACA,QAA+BtQ,IAAKA,IACpCsQ,6BAEAiwC,sBACAjwC,8BACA,GAEAA,iBACA,CACA,GAEAA,8BACA,kBACAA,uBAGA,4BAGA,yDAEA,QADAiwC,KACAvgD,IAAqCA,IAAIA,IACzCsQ,2BACAiwC,iBAGA38B,yBACA,mBACAA,iBACA,CAEA,IADAtT,aACAtQ,IAA+BA,IAAKA,IACpC4jB,iCACAtT,qBACA,iCACAA,aACAA,sBAIA,SACAi5B,KACA,QAA+BvpC,cAAoBA,IACnDspC,0BAEA,kBAA8CtpC,IAAKA,IACnDupC,0BAGA,SACAiX,iBACAA,iBACAlwC,oBACAA,gBACA,CACA,GAAa,GAEbA,uBACAA,qBACAA,oCACA,EAEAA,wBACAA,qBACAA,oCACA,CACA,EAEA,GAEAy7B,oEACA,OACAK,aACA7tB,2CAEAtI,qBAIA,OACAwqC,MACAvS,+BACAwS,2BAGAC,kBACAlR,oDAEAkC,UAEA/E,iCAEA6C,uDAGA8E,iBACAnE,oBACA,EACA,EAEA,EAEA,IAEArE,8DACA,OACAK,aACAjhC,gEACAy1C,aACAC,YACAvwC,OACAwwC,eAIA7qC,qBAEA,OACAyG,uBACAqhB,2CACAgjB,kCACAC,0BACAnjC,0BACAojC,wBACAC,mBAEAC,UACAxwC,sBAGAwwC,SADAtV,iCACA,gBAEAsV,uBACAA,yBAEA7wC,SACA6wC,WACA,EAAS/1C,iCAETC,gBACAA,4BACA,KAGA,sDACA+1C,gBAEA,SACAC,KACA,+BAGA,IAFA,kBACAA,UACArhD,yCACA,uBAEA,gBAAgEqS,SAChEivC,QAAoDjvC,SACpDgvC,UACA,CACA,GACAC,CACA,EAGA,2BASA5hD,uCAAsE9N,OARtE,CACA4f,uBACA+L,0BACAL,SACAmD,QACAkhC,iBAIA1hD,iBACAwL,4CAA4F,OAAQiD,cACpG,QACA,GAAkB,uBAClBuG,iBAAyC5jB,mBAAwB,YACjEoO,eACAgM,wCAAsF,OAASiD,cAC/F,QAAqB,GACH,gCAGlB,yEACA,IACAkzC,EADAC,8BAEAp2C,iCACA,KAEA,CACA,OACAyO,UACAzH,MAHAmvC,GAAuC9tB,sDAOvCguB,cAAuD,oCACvDC,KACA,2BACAA,uCAA8F/vD,QAASkuD,0BACvGjgD,iBACA,IAaA+hD,EAbAC,yBACA,OAAqDnuB,yCACrD,GAAyCnS,uBACzC,uBACA,wBACAugC,UAEAC,yBAGAD,CACA,EAAyC,IAGzC,UACAD,YACAD,gBACA,YAAwD5hD,WAA6BA,IACrF4hD,mBACAI,qBAEA,cACA,CACA,wBACAj8C,sBACAk8C,aACAD,cACA,GACAZ,IAEA,EACA,GACA3hD,qCACA4L,oCACAA,yBACA,EAAyB,CACzB,EAGA,EACA,EAEA,GAEA,IACA0gC,yDAMA,OACAK,aACA+D,wCACA7/B,OACA4xC,sCACAC,aAEAlsC,uBACA,WAEA26B,yBACAwR,YAEAC,aACA/xC,yCACA1B,oBACA,EACA,EAEA0zC,4BAEA3rB,yCAEA2rB,IACA,gCACA1R,6BACA2R,8CACA3R,6BAEAz1B,mBAGAonC,KACAF,IAEA/9C,aACA+9C,GACA,IAIAC,MACA,EACA,EAEA,IACAvW,uCACA,OACA91B,qBACA,IACA89B,6BACAA,qBAEApjC,mCADAojC,2CACA,kDAEApjC,uEAGAA,mBACAk7B,uBACAkI,sBAEAA,oBACAzjC,uBAEA,gDACA0jC,4CACAC,sCAEAtjC,4JACAA,kHACA,EACA,EAEA,GAEAo7B,wCAIA,oFASA,OACAK,aACA97B,OACA2wB,yBAIAhrB,qBACA,sBACAtF,YACA,MAjBA,cACA,wBACA,gCACAiF,gCACAA,yBACA,CAYA4sC,IACAC,8CACAC,8CACAC,eACAC,gBACAtyC,2BACA,cACA,SACAqyC,UACAh0B,OACAi0B,6CAEAF,iBACAD,UACA,EACA,EAEA,GAEA1W,0FAEA,OACA5gC,4DACAmF,OACAkmC,UACAqM,iBAGA5sC,qBAEA3F,gCACAA,QACAwyC,sBAEA,gFACAC,0CAGAzyC,eACAA,gBAEAA,mCACA,KACA,uDACA3f,8BAEA,MACA,SACAqyD,KACA1yC,cACA,SACA2yC,4BACAJ,wBAOA,GANAG,uCAAsF/xD,SACtF,YAEAiyD,yBACA,GAA6BjqC,UAE7B,OACA,qDACAiB,sBACA,4CACA,CACA5J,mCACA6yC,sCACAtuC,oBAAyD5jB,yBAAqC,YAC9FmyD,eACA9yC,yCACA8yC,cACA9yC,yCACA+yC,iBACAA,yBAEAA,qBACArjD,8BACAsQ,gBACA3f,4DACA0yD,MACA/yC,mBAAuExe,mBACvE,GAEA,EACA,EACA,GACA2N,yBAEAyjD,qBACA,gDACAphD,yCACA,GACAwO,8BACAA,qCAEA/b,cACA+uD,sBACA,oBAAmEj7C,OACnE,iBACAk7C,WAEAA,cACAR,sBACAQ,aACAA,iBAAsE1mB,0BAEtE,GACAtoC,2BACA,CACA,EACA,EACA,EACA,EACA,EACA,EACA,EAEA,IAEAw3C,yCACA,OACAxtB,8SAIAjO,OACA+J,UACAtL,SACAvN,UACAs4C,WAEA7jC,qBACA,OACA,CAAiBd,sBACjB,CAAiBA,sBACjB,CAAiBA,uBAIjB7E,WADAA,YAIAA,4BAGAA,cACAA,4BACAsT,GACA4/B,sBACAlzC,yCACA,SACAA,QACAmzC,iBACAC,wBAGAD,iBACAC,wBAEAD,iBACAC,wBACAA,UACApzC,iBACA6E,UACAwuC,gBACA7kD,oBAGA,EACA,EAEA,EACA,EAEA,GAoDAitC,4DACA,QACA,OACA5gC,0BAGA,WACA,sEACA,EACAmF,OACA1C,iBACAg2C,wBAEA/H,wBACA,8BAEA,CACAgI,oBAEA,EACAp+B,qBACAnV,WACAogC,iCAGA,EAEA,EAEA,IAEA3E,iEACA,OACAxtB,YACAjO,SACA87B,aACAyP,wBACA,OACAgI,oBAGAnT,QAFA7E,WACAA,sBAGA,EACApmB,qBAGA,EAEA,EAEA,IAWAsmB,yDACA,OACAK,aACA97B,SACA2F,sBACA6tC,4CACAtxD,wBACA,EAEA,IAEAu5C,wDACA,OACAK,aACA97B,SACA2F,qBACA,IACA8tC,GADAD,4CACAjzD,sBACAkzD,GACA7zD,gCACA,oEACA2L,OAEA,EAEA,EAEA,IAEAkwC,qCACA,OACAK,aACA+D,mBACAl6B,uBACA,KAEA,SACAqhB,uCACA0sB,IACAnzB,aACA,GACAA,oCACA,0BACA,EACA,EAGA,GAGAkb,oCACA,OACA5gC,oEACAihC,aACA97B,OACAtQ,WACA8V,aAEAG,qBAgCA,MA/BA3F,2BACAA,sBACAA,qBAGAA,OACAA,KACA2zC,QAA6Bx3C,OAC7By3C,kBAIA5zC,iBACAA,qBACAA,YACAA,YAAkC7D,QAElC6D,qBACAA,gBAAoC7D,MACpC,EACA6D,yBACAA,qBACA,EAEAA,0BACAA,uBACA,EACAA,8BACAA,0BACA,GAGA6zC,aAGA7zC,WACAA,8BACA3C,oBACA,GAIA,QAFAy2C,KACAC,KACArkD,IAA8BA,wBAA6BA,KAC3D,wBACA8B,UACAuiD,YACAD,sBAKA9zC,UAEA,CAEAA,UACAA,cACAA,oBACAA,iBAEAA,qBACAA,0BAA0D,oBAAwByc,UAClF,SACAs3B,kBAAgD,eAAiBt3B,eACjE,QAEA,KAIA,kCACAnJ,wBAEAugC,GACA,GAAiB,GACjB7zC,mBAGA,EAEA,GACAy7B,sCACA,OACA5gC,sEACAihC,aACA97B,OACAtQ,WACA8V,YACAwuC,cAEAruC,qBAiBA,MAhBA3F,2BACAA,sBACAA,qBAGAA,OACAA,KACAvG,yBACA1R,UAGAiY,WACAA,aACAA,cACAA,+BAGA6zC,aAOA,GAHA7zC,+CAEAA,mBACAA,SACAA,qEACAA,wBACA,6BAGAA,UACA,CACA,KAIA,kCACAsT,wBAEAugC,GACA,GAAiB,GACjB7zC,mBAGA,EAEA,GAEAy7B,oCACA,OACA5gC,oEACAihC,aACA97B,OACAtQ,WACA8V,YACAwuC,cAEAruC,qBAkCA,MAjCA3F,2BACAA,sBACAA,qBAGAA,OACAA,KACAne,SACAwxB,aAAkC0gC,oBAGlC/zC,iBACAA,qBACAA,SACAA,cAEAA,sBACAA,uBACA,EACAA,0BACAA,0BACA,EAEAA,4BACA,sBACA,8BACAA,qBAAiD+zC,SAEjD/zC,0BACAA,wBACA,CACA,IAGA6zC,aAEA,MA0CA,GAzCA,4BACA,0BACAE,yBACA/zC,mCAGA+zC,qCAAyE,gBAAet3B,yBACxFzc,WACAA,mCAAiE6F,0BAGjE,cACA,0BACAkuC,qCACA/zC,mCAGA+zC,0CAA8E,gBAAet3B,kBAC7Fzc,WACAA,mCAAiE6F,0BAGjE,aACA,yBACA7F,YAGA+zC,oEACA/zC,4DAGA,gBACA,yBACAA,YAGA+zC,gEACA/zC,6DAGAA,mBACAA,SACAA,WACAA,cACA,mCACA8zC,sBAGA,GACA9zC,gBACA,CAEA,KAIA,kCACAsT,wBAEAugC,GACA,GAAiB,GACjB7zC,mBAGA,EAEA,GAGAy7B,8CACA,OACA5gC,gFACAihC,aACA97B,OACAtQ,WACAskD,cAEAruC,qBAqEA,MApEA3F,2BACAA,sBACAA,qBAGAA,OACAA,KACAne,sCACAoyD,6BACAC,UACAC,SACAC,YAGAp0C,SACAA,0CACAA,iCACAA,cAGAA,4BACA,iEACAA,mBACAA,cAEA,iFACAA,aACAA,cAEA,iEACAA,sBACAA,cAEA,sBACAA,mBACAA,oBAEA,0BACAA,aACAA,oBAEA,sBACAA,sBACAA,oBAGA,0FACAA,mBACAA,2BAEA,8GACAA,aACAA,2BAEA,0FACAA,sBACAA,8BAGA,QACAA,oBACAA,cAGAjR,oBAEA,IAGA8kD,aACA,cACA7zC,WACA,iBACAq0C,aAEA,qBAOA,wBANA,6CACAr0C,4BACA9G,aAEA8G,YASA,uBACA,6CACAA,mBACA9G,kBACAo7C,4BAKAt0C,YAGA,qBACA,6CACA,0CACAsM,aAEAtM,aAEA,6DACA,6CACAA,mBACAsM,kBACA+nC,4BAKAr0C,YAGAA,mBACAA,UACAA,iEACA,4BACAA,2BACAA,cACAA,aACAA,aACAA,oBACAA,oBAEA,KAIA,kCACAsT,wBAEAugC,GACA,GAAiB,GACjB7zC,mBAGA,EAEA,GAEAy7B,+CACA,OACA5gC,iFACAihC,aACA97B,OACAtQ,WACAskD,cAEAruC,qBAiEA,MAhEA3F,2BACAA,sBACAA,qBAGAA,OACAA,KACAne,kCACAoyD,gCACAC,UACAC,SACAC,YAGAp0C,SACAA,sCACAA,oCACAA,cAGAA,4BACA,yDACAA,sBACAA,cAEA,yEACAA,aACAA,cAEA,oBACAA,sBACAA,oBAEA,wBACAA,aACAA,oBAGA,gFACAA,sBACAA,qBAEA,oGACAA,aACAA,qBAGA,wFACAA,sBACAA,cAEA,4CACAA,uBACAA,cAEA,QACAA,oBACAA,cAGAjR,oBAEA,IAIA8kD,aAEA,cACA7zC,WACA,iBACAq0C,aACA,wBACA,6CACAr0C,4BACA9G,aAEA8G,YAEA,yBACA,oEACA,6BACA9G,aAEA8G,YAEA,0BACA,oEACA,oCACA9G,aAEA8G,YAEA,uBACA,6CACAA,mBACA9G,kBACAo7C,4BAKAt0C,YAGA,qBACA,6CACA,0CACAsM,aAEAtM,YAEA,uBACA,6CACAA,mBACAsM,kBACA+nC,4BAKAr0C,YAGAA,mBACAA,UACAA,8CACA,4BACAA,2BACAA,cACAA,aACAA,aACAA,oBACAA,oBAGA,KAKA,kCACAsT,wBAEAugC,GACA,GAAiB,GACjB7zC,mBAEA,EAEA,GAEAy7B,kDACA,OACA5gC,oFACAihC,aACA97B,OACAtQ,YAEAiW,qBACA3F,2BACAA,sBACAA,qBAGAA,OACAA,KACAne,eACAoyD,eACAC,YAGAl0C,SACAA,mBACAA,oBAGAA,kBAEAA,4BACA,+BACAA,gBACA,CAAyB7D,uBACzB,CAAyBA,yCACzB,CAAyBA,uCAGzB,+BACA6D,gBACA,CAAyB7D,mCAGzB,qEACA6D,gBACA,CAAyB7D,wBAGzB,QACA6D,kBACAA,qBAGAA,iBAEA,GAGAA,2BAEA,sBACAA,WACA,oBACA+zC,qBAEA,mBACA,gBACAA,qBAEA/zC,WAEA,oBACA,0CACA+zC,aAEA/zC,WAEA,0CACAA,4BACA+zC,aAEA/zC,WAEA,sBACA,6CACAA,mBACA+zC,WACAO,qBAKAt0C,YAGAA,WAEAA,mBACAA,UACAA,uCACAA,cACAA,aACAA,oBAGA,EAEAA,kBAEA,kCACAsT,wBAEAtT,iBACA,GAAiB,GACjBA,4BAEAqmB,GACA,EAEA,EAEA,GAEAoV,sCACA,OACA5gC,uEACAihC,aACA97B,OACA9G,WACAq7C,cACAC,iBACAC,kBACAC,YAEA/uC,qBACA,MAEAgvC,cAIA,OAFA30C,WACAvR,SACA,sBAGA4O,6GAGAA,oHAFA,CAA4BwH,qDAK5BxH,yFAGAA,gGAFA,CAA4BwH,iDAK5BxH,oEAGAA,2EAFA,CAA4BwH,+CAK5BxH,2GACA,CAA4BwH,oDAE5BxH,wFACA,CAA4BwH,4CAK5B,EAEA7E,KAAuB6E,2CACvB,aACAgB,IACA7F,cACAA,cACAA,gBACAA,iBAEAA,iBACAA,sBACAA,eAEAA,iBACAA,gBACAA,4BAIAA,2BACA,8BAEA,UACAA,cACAA,WACAvR,QACAA,YACA,gCACAA,SACA,6BACA,8BAGAuR,gBAEAvR,EADAuR,+BACAvR,8BAEAA,sBAEAuR,iBACAvR,qCAGAkmD,MACA30C,MACAA,aAGAA,OACAA,YAEA,GAAa,EAEb,EAEA,EAGA,CA/2LA,GCDA,WACA,aAEApgB,uCACAC,uDACA,IACAuP,EADAwlD,KAGA,OACAC,sBACAD,QACA16C,SACArB,YAEA,EACAi8C,qBACA1lD,oBAIA,QAHAs1B,cAA8C,sBAAgBqwB,sBAA6BA,oBAC3FC,cAA4C,sBAAgBD,qBAA4BA,oBAExFrlD,IAAyBA,WAAgBA,KACzC,gBACAN,qBACA,CACA,EACA6lD,sBACA,YAAyBvlD,WAAgBA,KACzC,cACAwlD,uBACA,CACA,EACAC,iBACA,4BACAj7C,EACA,EAGA,IAEA49B,8CAoBA,kBACA,qBACA,EACAjpC,8BACA,WACAA,IACA,EAGAA,+BACA,OAQAA,IACA,EAGAA,iCAEA,OACAA,IACA,EAEA,SAEAA,oCACA,aACAA,IACA,EAEA,OACAomD,yBACAH,qBACAM,8BACAC,iCAEAxmD,wBACA,2BACAA,IACA,EAGAA,iFAGA,oBACAiG,6BACAwgD,aACA,GAEAC,aACAzgD,6BACAwgD,aACA,GAGA,CAyBAE,0BACA,mBACA,mCACA,8BAgBA,mBAbA,kBAEA,IADAC,qCAAsGJ,oBACtGK,GACA,gBACA,6BACA78C,SACA,CAEA,sCACAy8C,gBAMA,EAEA,EAEA,EAKA,CA/KA,GCCA,WACA,aAEA11D,wCACAk4C,wDACA,WAEAjpC,gBACAA,mBACAA,oBAEAA,qBACAA,2BAEAA,4BAEAA,sBAEA,oBACAG,oBACAA,sBACAA,yBACA,EAuCA,iCACA,wBACAsP,cACAq3C,0BACAC,SACAC,iBACAC,eAEA,EAEAjnD,4BAEA,IAiGAgnB,EAjGAkgC,EAjDA,YACA,SACAC,eACA,mBACAp2D,4CAA+Dq2D,YAG/DD,mCAAkDC,yBAElDA,CACA,CAuCAC,YAEAC,qBACA,mBACA,WACAtnD,uBACAjP,kDACAm2D,SACA,EAEA,CAMA,GAJAlnD,yBAIA,qBACA,sBAAqDmK,WAAYC,aAGjEm9C,GACAv1D,UACAkY,MACAs9C,aALAC,sBAOApuC,SACAquC,gBAAuC,UACvCC,4CACA,0CAAwF3tB,oBACxF,KAgBA,GAZAytB,WACA12D,oCAEA02D,eACAF,2BAEAE,WACAF,mBAEAE,mBACAF,mCAEAE,mBACAF,8CAEAE,cACAF,kEAEAE,WACAF,8BAEAE,YACAF,+CAGA,OA/FA,kBACA,wBACA,uBACAK,4BAEA,0EACApf,KACA,4CACAxT,uBACAwT,sCAEA,GAEAA,WACAxkB,cACApkB,OAEAA,OACAA,gBAEAA,kFAGA,CAwEAioD,+BACA,QAAiB,IAEjBJ,QAIA,kIAEA5oC,qBACA,CAGA,aACA,wBAA6C,CAC7C6/B,iBACAjvC,cACAq3C,0BACAgB,iCACaL,UAEbM,mCACAA,4DAEA5nD,wBAEA,2BAEAA,mBACAA,qBACAA,0BAGA,wBACAA,6BACA,CAGA,gBAEA,iCACA6nD,gBAAyDhhC,0CAAiD,GAE1G,8BACAghC,0CAGA,iCACA,gBACA,qFAGAA,EADAhhC,uBACA,CACAy6B,YACA/C,iBACAuJ,0BACAf,mBAEA,SAEAO,sBACAzgC,0CAEAghC,KACAvG,YACA/C,iBACAuJ,wBAA+C,UAC/Cf,wBACiB,KAEjBO,YASA,8FARAzgC,0CAEAghC,KACAvG,WACA/C,kBAIA,MAEA,IACA,iBACA,4DAGA,kCACA1+C,4BAEA6e,yBACA7sB,2BACAkY,MAAwBs9C,kCACxBnuC,SACAquC,wCAAkF,WAClFC,4CACA,mCAAqEx9C,WAAYC,aAAa,MAAS4vB,oBACvG,IAGAkuB,6CAEAlzB,+BACAp1B,2JAEAo1B,sHACAA,OACA,kBACAp1B,aACAA,kFAEA,MAKAO,CACA,EAGAH,mGACA,OAEAN,uBACA,uBACA,EAEAA,6BAEA,mBADAS,iBACA,YAAsD,2BACtD,EAEAT,6BAEA,mBADAS,iBACA,YAAsD,mBACtD,EAEAT,4BACA,IAGAyoD,KACA,uBAJAhoD,iBAIA,YACAgoD,aACAA,eACAA,kBACA,GACAA,CACA,EAEAzoD,iCACA,aACA,gCACA,SAEAsnB,qCAGAA,wBAIA,OAA6BrsB,wBAK7B,oEAAyG,CACzG,KAGA0S,wBAA2DyP,0BAI3D,CACA,8BAA4DniB,wBAAgC,CAAGmiB,sBAE/F,mCACA5c,kEAGAkoD,oBAEA,CACA,QACA,CAAcC,GACdh7C,UAKA,EAGA3N,2BACAA,uBACA3O,6CACA2O,uCAAqE,8BACrE,GACAA,yBAGA,6BAEA,MACA,iCAEA,IAUA0hB,EAVAknC,KACA,aAEA,sCACAv3D,8BACA,mBACAu3D,gCACA,EACA,CAIA,cACA,SACA,0CACA,wBACAxV,UACA,GACAA,CACA,CAEA,YAA0BjyC,eAA8BA,KACxD,sBACA,sBASA,GAPA2nC,YACAviC,0BACAuiC,YAKA,KAFApnB,wBAEA,CACA,SACArwB,iDAEA4rB,SACAlqB,iBACA+1C,KAGA,GACApnB,GACAs9B,iBACAjsD,UAKAiS,wBAGA0c,KAEA,CACA,MACAkmC,wBACAlmC,GAA+Bs9B,mBAG/B,MACA,kBACA,wDAAqG3tB,OAkD/Es3B,EACtBh7C,WAGAnN,kEAtDwH,CACxHA,4DACAA,iFAEA,gCACA0qB,qBAIA,IAHAD,0BACA09B,MAEAA,EAIAh7C,uBAAsEyP,iBAEtE,CAGAsE,yBACArwB,kCACAsc,6EACAnN,iDACAkhB,cAEA,GAEA,SACArwB,uCAAkFw3D,SAElF,wBAA4EzrC,sBAC5E5c,iFACA,mCACAA,kEAEA,2CACAA,yEAEA,6DACAA,+EAEA,kEACAA,4FAEA,mEACAA,4FAGAkoD,oBAEA,CACA,CAMA,QACA,CACA,CAEA,gCAAwD,CACxDloD,wEACA,iCAA2D,KAC3D,qBAAuC8mB,sBAAkCqhC,KACzE,CACA,gCACA,GArNA,OAuNA3oD,CACA,GAEAM,IACA,IAEAhP,qEACA,OACAw3D,WACAC,cACAC,SAIAhpD,sBACA,gBACAA,gBACAA,2BACAA,aACA,EAEAA,4BAEA,OADAipD,SACAC,kBAEAlpD,2BACAA,cACA,IAEAA,6BACA,wBACAA,cAEA,GACA,EAEAA,iBACA,6BACA,EAEAA,qCAGA,sCACA,MAEA,SAGA,WACAmpD,2BAEA,8BAKAA,oBAQA,EAEAnpD,sCACAA,oFACA,qCAEA,IACA8O,EADA3E,QACAw0C,8BACA,SAMA3+C,gBADA8O,KACA,GACAzd,qBAIA2O,+CACA,IAJAA,yDACA,IAOAA,8DACA,IAhBAA,mDACA,EAiBA,EAEAA,4BACAA,oCAEA1N,yBACA,qBAEA62D,sCAEA72D,oBACA0N,qCAGAyF,aACAzF,8CACAiG,4CACAA,6BAA6CmjD,cAAgB,0BAE7DC,sBAAsCD,cAAgB,yBAEtD,EAAS,EACT,GApGA,OAsGAppD,CACA,IAMAktC,8EACA,OACA6U,aACAC,YACAnP,qBACAtmC,wBACAywC,sBACA,oBAEA,2BACA,IACAsM,EACAC,EACAC,EAHAC,IAMAC,aACAH,IACAA,WACAA,QAEAD,IACAA,aACAA,QAEAE,IACAvR,2BACAsR,MACA,GACAA,IACAC,OAEA,EAEA/3C,uBACA,UACA,KAKA,0CACA,iBACAk4C,wDACA,eAEA33B,WADAq1B,WACA,wBAAmFuC,mBAAyB7sC,EAG5GA,EAIA,sBACA2sC,IACAzR,iBACA,GACAqR,IACAE,GAGA,EAAyB,WACzBK,OACAH,GAEA,OAEA,CACA,eACA9Y,cACA,oCAC6CgZ,gEACZA,+DACsCA,yBAEvE53B,WADAq1B,WACA,wBAA+EuC,mBAAyBhZ,EAGxGA,EAEA,sBACA8Y,IACAzR,iBACA,GACAqR,IACAE,GACA,EACA,EACA,CACA,EAEA,IAEAtc,gDACA,OACA6U,cACAzQ,sBACAl6B,uBACA,+BASA,OALAq+B,eACAlE,EAwCA,gBAIA,UAEApwC,EADA2oD,6BACAC,KAEA,uBAEAA,gCACI,CASJ,IAPAC,2CACA1zC,sDACAwlC,+BACAkO,8DAGA7oD,OACAA,KACA6oD,cAGAD,0BAEAC,gBACAC,cACA,CAGA,wBACAH,eACA5iD,sBACA4iD,gBACA,GAEAA,CACA,CA9EAI,oCACA,YACAzU,WACA,EAAqB,CAAG0U,wBAIxB1U,mBACAlE,kBACA,EAEA,IAEAzmC,gCACA,qBACA,SACA,0BACAvH,sBAGA,QAFA6mD,KACA7sC,iBACApc,IAA8BA,WAAeA,KAC7C,WACA4U,qBACA,wDACAq0C,KACA,KACA,CACA,CACAA,GACAzgB,SAEA,GAEAA,IAEAA,CACA,CACA,EAsDA,CAjxBA,GCDA,WACA,aAEAt4C,oGAEAg5D,EAGAC,kBACAv6C,uBACAq3C,aAAsBtiC,gBACtBxyB,aACAga,yBACAC,qCACAg7C,YACAjgC,mCAEA+/B,QACAkD,mBAGAD,2BACAv6C,kCACAq3C,aAAsBtiC,sBACtBxyB,uBACAk4D,wCACAjD,uBACA,0BACA,EACAF,QACArhB,eACAhjB,2GAGAsnC,kCACAv6C,kCACAq3C,aAAsBtiC,sBACtBxyB,oCACAk4D,wCACAjD,uBACA,uBACA,EACAF,QACArhB,eACAoiB,8EACA97C,oEACAi+C,mBAGAD,2BACAv6C,6BACAq3C,aAAsBtiC,YAAa7xB,aACnCX,sDACAk4D,+BACAjD,cACAF,QACArhB,cACAhjB,6CAGAqkC,wBACAt3C,gBACAq3C,aAAsBtiC,YAAa7xB,aACnC+yC,cACAhjB,6BACAynC,qBAAgC,+BAMhCH,4BACAv6C,mBACAq3C,aAAsBtiC,mBACtBxyB,kCACAk4D,gCACAjD,cACAF,QACArhB,cACAhjB,8DAGAsnC,6BACAv6C,oBACAq3C,aAAsBtiC,mBACtBxyB,kBACAia,sCACAD,8BACAi7C,cACAF,QACArhB,cACAhjB,+CAGAsnC,kCACAv6C,+BACAq3C,yBACA90D,yBACAia,sCACAD,2CACAi7C,cACAF,QACArhB,gBACAhjB,uIAIAsnC,mCACAv6C,6BACAq3C,aAAsBtiC,mBACtBxyB,wBACAia,4CACAD,oCACAi7C,cACAF,QACArhB,cACAhjB,sEAGAsnC,+BACAv6C,8BACAq3C,aAAsBtiC,sBACtBxyB,oBACAia,wCACAD,6CACAi7C,cACAF,QACArhB,aACAhjB,2DAGAsnC,6BACAv6C,0BACAq3C,aAAsBtiC,qBACtBxyB,kCACAk4D,gCACAjD,cACAF,QACArhB,gBACAhjB,0CAGAsnC,yBACAv6C,yBACAq3C,aAAsBtiC,uBACtBxyB,+CACAk4D,6BACAjD,cACAF,QACArhB,gBACAhjB,mEAGAsnC,2BACAv6C,mBACAq3C,aAAsBtiC,uBACtBxyB,wCACAk4D,+BACAjD,cACAF,QACArhB,gBACAhjB,6KAGAsnC,oCACAv6C,+BACAq3C,aAAsBtiC,qBACtBxyB,iCACAk4D,wCACAjD,cACAF,QACArhB,gBACAhjB,wGAGAqkC,2BACAt3C,6BACAq3C,aAAsBtiC,YAAa4lC,kBACnC1kB,aACAhjB,kFAEAsnC,kCACAv6C,+BACAq3C,aAAsBtiC,6BACtBxyB,0BACAk4D,sCACAjD,cACAF,QACArhB,aACAhjB,mEAGAsnC,kCACAv6C,gCACAq3C,aAAsBtiC,YAAa6lC,qBACnCr4D,uBACAk4D,6CACAjD,cACAF,QACArhB,aACAhjB,gPAGAsnC,mCACAv6C,gCACAq3C,aAAsBtiC,YAAa8lC,iBACnCt4D,yCACAk4D,8CACAjD,cACAF,QACArhB,aACAhjB,uIAGAsnC,uCACAv6C,6BACAq3C,aAAsBtiC,YAAa+lC,gBACnCv4D,yDACAk4D,2CACAjD,cACAF,QACArhB,aACAhjB,kFAGAsnC,2BACAv6C,0BACAq3C,aAAsBtiC,sBACtBxyB,gDACAk4D,wCACAjD,cACAF,QACArhB,gBACAhjB,mEAGAsnC,gCACAv6C,+BACAq3C,aAAsBtiC,yBACtBxyB,+CACAk4D,mCACAjD,cACAF,QACArhB,gBACAhjB,4JAGAsnC,iCACAv6C,8BACAq3C,aAAsBtiC,yBACtBxyB,uCACAk4D,qCACAjD,cACAF,QACArhB,gBACAhjB,6FAGAsnC,gCACAv6C,8BACAq3C,aAAsBtiC,qBACtBxyB,2CACAk4D,mCACAjD,cACAF,QACArhB,mBACAhjB,8FAGAqkC,8BACAt3C,oBACAq3C,aAAsBtiC,YAAagmC,aACnC9kB,mBACAhjB,0CAEAqkC,2BACAt3C,qBACAq3C,aAAsB2D,mBACtB/nC,yCACAgjB,qBAEAqhB,0BACAt3C,sCACAq3C,aAAuB2D,WACvB/nC,kDACAgjB,qBAIAskB,oBACAv6C,gBACAq3C,aAAsB4D,aACtB14D,eACAga,2BACAC,8BACAg7C,YACAjgC,qCAEA+/B,QACAkD,mBAGAlD,4BACAt3C,oBACAq3C,aAAsB4D,eACtBhlB,eACAhjB,qDAEAqkC,0BACAt3C,uBACAq3C,aAAsB4D,eACtBhlB,eACAhjB,qDAEAsnC,iCACAv6C,8BACAzd,uGACA80D,aAAsB4D,eACtBR,uCACAjD,cACAF,QACArhB,eACAhjB,2CAGAsnC,+BACAv6C,mBACAzd,8BACAk4D,mCACApD,sBACAG,cACAF,QACArhB,mBACAhjB,4EAGAsnC,iCACAv6C,qBACAzd,oBACAk4D,wCACApD,sBACAG,cACAF,QACArhB,cACAhjB,mDAGAsnC,kCACAv6C,sBACAzd,qBACAk4D,wCACApD,sBACAG,cACAF,QACArhB,cACAhjB,2DAGAsnC,+BACAv6C,0BACAzd,kBACAk4D,qCACApD,sBACAG,cACAF,QACArhB,cACAhjB,4DAeAsnC,qCACAv6C,0BACAzd,wBACAk4D,yCACApD,sBACAG,cACAF,QACArhB,cACAhjB,oEAGAsnC,qCACAv6C,yBACAzd,wBACAk4D,sCACApD,sBACAG,cACAF,QACArhB,iBACAhjB,gDAGAsnC,mCACAv6C,uBACAzd,sBACAk4D,sCACApD,sBACAG,cACAF,QACArhB,iBACAohB,yBAGAkD,sCACAv6C,4BACAzd,yBACAk4D,uCACApD,sBACAG,cACAF,QACArhB,YACAhjB,sDAGAsnC,oCACAv6C,8BACAzd,uBACAk4D,uCACApD,sBACAG,cACAF,QACArhB,YACAhjB,gDAGAsnC,+BACAv6C,mBACAzd,kBACAk4D,kCACApD,sBACAG,cACAF,QACArhB,cACAhjB,2CAGAsnC,wCACAv6C,6BACAzd,uCACAk4D,sDACApD,sBACAG,cACAF,QACArhB,cACAhjB,kDAGAsnC,wCACAv6C,gCACAw3C,cACAD,iBACAD,QACA3nC,oKACAsmB,kBACAukB,gBACAE,qBAAoC,uCAGpCH,kCACAv6C,4BACAw3C,cACAD,iBACAD,QACA3nC,4HACAsmB,kBACAukB,gBACAE,qBAAoC,uCAKpCH,4BACAv6C,wBACAzd,2BACAk4D,gCACApD,sBACAG,uBACA,gCACA,EACAF,YAGAiD,iCACAv6C,6BACAzd,+BACAk4D,qCACApD,sBACAG,cACAF,YAEAiD,gCACAv6C,4BACAzd,8BACAk4D,oCACApD,sBACAG,cACAF,YAEAiD,qCACAv6C,kCACAzd,wBACAk4D,2CACApD,sBACAG,cACAF,YAEAiD,mCACAv6C,uBACAzd,uCACAk4D,sCACApD,sBACAG,cACAF,YAKAA,kBACAt3C,qBACAq3C,sBACAphB,cACAhjB,6BASAsnC,iBACAv6C,oBACAzd,YACAga,wBACAC,kCACA66C,aAAsBrpB,eACtBwpB,YACAjgC,wCAEA+/B,YAEAiD,0BACAv6C,kBACAzd,gBACAotB,mFACA0nC,aAAsBrpB,eACtBwpB,cACAF,QACArhB,gBAGAskB,yBACAv6C,iBACAzd,wBACAS,QACAmnD,SAAuB+Q,wBAEvB7D,aAAsBrpB,eACtBzxB,gCACAC,kCACAg7C,YACAjgC,+BAEA+/B,QACArhB,gBAGAskB,8BACAv6C,4BACAzd,oBACAga,qCACAC,kCACAg7C,cACAH,aAAuBrpB,0BACvBspB,QACArhB,gBAGAskB,gCACAv6C,iBACAzd,eACAotB,iCACA0nC,aAAsBrpB,eACtBwpB,cACAF,QACAkD,gBACAnC,oEACApiB,iBACA15B,8DACA4+C,eAGAZ,uBACAv6C,cACAzd,uCACAk4D,2BACApD,aAAsBrpB,eACtBwpB,gBAEA+C,0BACAv6C,iBACAzd,gBACAk4D,qCACApD,aAAsBrpB,sBACtBwpB,cACAF,QACArhB,gBAGAskB,iCACAv6C,wBACAzd,sHACAk4D,6BACApD,aAAsBrpB,gCACtBwpB,cACAF,QACArhB,gBAGAskB,8BACAv6C,mCACAzd,2BACAS,QACAX,SAEAo4D,yBACApD,aAAsBrpB,iCACtBwpB,cACAF,QACArhB,gBAGAskB,2BACAv6C,2BACAzd,6CACAk4D,+BACApD,aAAsBrpB,eACtBwpB,uBACA,sBACA,EACAF,QACArhB,mBAGAskB,6BACAv6C,oBACAzd,mBACAga,oCACAC,qCACA66C,aAAsBrpB,eACtBwpB,YACAjgC,uCAEA+/B,QACArhB,mBAGAskB,kCACAv6C,8BACAzd,wBACAk4D,qCACApD,aAAsBrpB,eACtBwpB,cACAF,QACArhB,mBAGAskB,iCACAv6C,wBACAzd,uBACAk4D,oCACApD,aAAsBrpB,eACtBwpB,cACAF,QACArhB,mBAGAskB,2BACAv6C,6BACAzd,4BACAk4D,wCACApD,aAAsBrpB,eACtBwpB,cACAF,QACArhB,mBAGAskB,0BACAv6C,iBACAzd,iLACAk4D,6BACApD,aAAsBrpB,eACtBwpB,cACAF,QACArhB,gBACAhjB,oHAGAsnC,kCACAv6C,uBACAzd,4DACAk4D,+BACApD,aAAsBrpB,eACtBwpB,uBACA,mBACA,EACAF,QACAkD,gBACAnC,oEACApiB,iBACA15B,mEACA4+C,eAMAZ,yBACAv6C,oBACAzd,gFACAk4D,6BACApD,aAAsBrpB,eACtBwpB,cACAF,YAEAiD,0BACAh4D,oEACAk4D,kCACAjD,cACAF,YAEAiD,yBACAv6C,qBACAzd,6BACAk4D,6BACApD,aAAsBrpB,eACtBwpB,cACAF,YAEAiD,kCACAv6C,8BACAzd,4CACAk4D,qCACApD,aAAsBrpB,eACtBwpB,cACAF,YAEAiD,gCACAv6C,mCACAzd,sBACAk4D,yCACApD,aAAsBrpB,eACtBwpB,cACAF,YAKAiD,oBACAv6C,gBACAzd,eACAga,2BACAC,8BACA66C,aAAsBpjB,eACtBujB,cACAF,YAEAiD,2BACAv6C,8BACAzd,cACAk4D,qCACApD,aAAsBpjB,eACtBujB,cACAF,YAEAiD,2BACAv6C,uBACAzd,2KACAk4D,oCACAz3D,QAAkBO,UAClB8zD,aAAuBpjB,WAAavwC,aACpC8zD,uBACA,iBACA,EACAF,YAEAiD,+BACAv6C,6BACAzd,kBACAga,sCACAC,gCACA66C,aAAuBpjB,oBACvBujB,cACAF,QACAkD,gBACAvkB,gBACAykB,qBAAoC,0CAGpCH,2BACAv6C,uBACAzd,qeAMAk4D,+BACApD,aAAsBpjB,eACtBjxC,QACAo4D,aAEA5D,cACAF,QACAe,oEACA97C,mEACA4+C,YACAX,mBAGAD,8BACAv6C,0BACAzd,4CACAk4D,iCACApD,aAAsBpjB,eACtBujB,cACAF,YAYAiD,mBACAv6C,eACAzd,8BACAga,2BACAC,6BACAg7C,gBAGA+C,4BACAv6C,kBACAzd,6FACAk4D,iCACApD,aAAsBrpB,eACtBwpB,uBACA,UAA0Bze,GAC1B,0BACA/1C,iBACAA,WAEA,CACAisD,8BACAjsD,SAGA,EAEAs0D,QACAe,oEACA97C,+DACA4+C,YACAX,mBAGAD,2BACAv6C,gBACAzd,2DACAk4D,mCACApD,aAAsBpjB,eACtBujB,cACAF,YAGAiD,oCACAv6C,gCACAzd,2CACAk4D,sDACAjD,cACAH,aAAsB7rB,0BACtB8rB,YAEAiD,6BACAv6C,iBACAzd,qCACAk4D,kCACApD,aAAsBpjB,eACtBujB,cACAF,YAEAiD,uCACAv6C,wBACAzd,4DACAk4D,4CACApD,aAAsBpjB,eACtBujB,cACAF,YAEAiD,4BACAv6C,wBACAzd,gBACAk4D,+CACApD,aAAsBpjB,eACtBujB,cACAF,YAEAiD,+BACAv6C,2BACAzd,0BACAk4D,qCACApD,aAAsBpjB,eACtBujB,cACAF,YAEAiD,8BACAv6C,0BACAzd,uBACAga,sCACAC,uCACA66C,aAAsBpjB,eACtBujB,cACAF,YAEAiD,0BACAv6C,sBACAzd,2DACAk4D,kCACApD,aAAsBpjB,eACtBujB,cACAF,YAEAiD,wBACAv6C,oBACAzd,sFACAk4D,oCACApD,aAAsBpjB,eACtBujB,cACAF,YAEAiD,+BACAv6C,2BACAzd,sBACAk4D,wCACApD,aAAsBpjB,eACtBujB,cACAF,YAEAiD,yBACAv6C,qBACAzd,aACAga,iCACAC,kCACA66C,aAAsBpjB,eACtBujB,cACAF,YAEAiD,8BACAv6C,mBACAzd,kBACA80D,aAAsBpjB,eACtB13B,sCACAC,sCACAg7C,cACAF,YAEAiD,kCACAv6C,gBACAzd,6CACAk4D,mCACApD,aAAsBpjB,eACtBujB,cACAF,YAEAiD,yBACAv6C,qBACAzd,aACAga,iCACAC,kCACA66C,aAAsBpjB,WAAaonB,YACnC7D,cACAF,YAEAiD,0BACAv6C,sBACAzd,cACAga,kCACAC,mCACA66C,aAAsBpjB,WAAapwC,aACnC2zD,cACAx0D,QACAo4D,aAEA9D,YAEAiD,oCACAv6C,gCACAzd,6CACAk4D,yCACApD,aAAsBpjB,eACtBujB,cACAF,YAKAiD,kBACAv6C,cACAzd,aACAk4D,iCACAl+C,yBACAC,4BACA66C,aAAsB7rB,WACtBgsB,cACAF,QACAkD,mBAGAD,uBACAv6C,aACAzd,sBACAk4D,gCACApD,aAAsB7rB,mBACtBgsB,cACAF,QACArkC,8DAGAsnC,2BACAv6C,iBACAzd,6CACA80D,aAAsB7rB,uBACtBivB,+BACAjD,cACAF,QACArkC,wDAGAsnC,2BACAv6C,iBACAzd,6BACAS,QACAs4D,eAEAb,uCACApD,aAAsB7rB,2BACtBgsB,cACAF,QACArkC,0CAGAsnC,uBACAv6C,aACAzd,qFACAk4D,4BACApD,aAAsB7rB,2BACtBgsB,cACAF,QACArkC,sCAGAsnC,+BACAv6C,qBACAzd,iDACAS,QACAu4D,cAEAd,mCACApD,aAAsBpjB,mBACtBujB,cACAF,QACArkC,uDAGAsnC,oCACAv6C,0BACAzd,yBACAk4D,wCACApD,aAAsB7rB,2BACtBgsB,cACAF,QACArkC,6DAGAqkC,0BACAt3C,0BACAq3C,aAAsB7zB,eAAgBC,sBACtCxQ,kDAMAqkC,0BACAt3C,iCACAq3C,aAAsB7zB,eAAgBg4B,YACtCvoC,wDAEAqkC,wCACAt3C,8BACAq3C,aAAsB2D,OAAQS,iBAC9BxoC,2GAMAsnC,6BACAv6C,0BACAzd,kBACAk4D,wCACApD,aAAsB7rB,2BACtBgsB,cACAF,QACArkC,qEAGAsnC,kCACAv6C,+BACAzd,uBACAga,yCACA86C,aAAsB7rB,yBACtB5hB,SACA8xC,kEACA,kBACA,IAEAl/C,oCACAg7C,cACAF,QACArkC,wEAGAsnC,8BACAv6C,gCACAzd,8CACAS,QACA24D,sBAEAlB,yCACApD,aAAsB7rB,4BACtBgsB,cACAF,QACArkC,wFAGAsnC,yBACAv6C,sCACAzd,cACAk4D,6BACApD,aAAsB7rB,wBACtBgsB,cACAF,QACArkC,wDAGAsnC,6BACAv6C,2BACAzd,kBACA80D,aAAsB38C,oBACtB6B,oCACAC,uCACAg7C,cACAF,QACArkC,uCAGAsnC,iCACAv6C,uBACAzd,6BACAk4D,uCACApD,aAAsB7rB,wBACtBgsB,cACAF,QACArkC,oCAGAsnC,iCACAv6C,uBACAzd,sBACAk4D,qCACApD,aAAsB7rB,kCACtBgsB,cACAF,QACArkC,4EAGAsnC,uCACAv6C,6BACAzd,yDACAk4D,qCACApD,aAAsB7rB,oCACtBgsB,cACAF,QACArkC,gEAGAqkC,0BACAt3C,0BACAq3C,aAAsB7rB,OAAQowB,mBAC9B3oC,+FAEAqkC,6BACAt3C,sBACAq3C,aAAsB7rB,+BACtBvY,gEAEAsnC,iCACAv6C,uBACAzd,qBACA80D,aAAsB7rB,wBACtBjvB,wCACAC,oCACAg7C,cACAF,QACArkC,kDAGAsnC,gCACAv6C,iCACAzd,qBACAk4D,mCACApD,aAAsB7rB,qCACtBgsB,cACAF,QACArkC,8DAGAsnC,6BACAv6C,mBACAzd,uEACAk4D,8BACApD,aAAsB7rB,0BACtBgsB,uBACA,SAGA,MAFA,yBACA,+BACA,0BACA,wCACA,CACAvI,sBACAjsD,WAEc,sBACd,0CACA,CACAisD,sCACAjsD,YAGA,qCACA,kCACA,CACAisD,+BACAjsD,UAGA,EACAs0D,QACArkC,wCAGAsnC,4BACAv6C,kBACAzd,iBACAga,mCACA86C,aAAsB7rB,0BACtBhvB,+BACAoN,SACAiyC,8BACA,yCACA,IAEArE,cACAF,QACArkC,+CAGAsnC,kCACAv6C,wBACAzd,uBACAupD,SACAtvC,uCACAs/C,cACA,GACAzE,aAAsB7rB,cACtBgsB,cACAF,QACArkC,oCACAynC,qBAAoC,wCAGpCH,+BACAv6C,oBACAzd,oBACAupD,SACAtvC,sBACA,8CACA,WAGA,yGACA,cACA,mBACAtG,YACA,2CACA,iIACA,MACAkM,6JAEA,EACAi1C,aAAsB7rB,cACtBgsB,cACAF,QACArkC,mCAGAsnC,4BACAv6C,8BACAzd,iBACAk4D,gCACApD,aAAsB7rB,mBACtBgsB,cACAF,QACArkC,iEAGAsnC,6CACAv6C,mCACAzd,0DACAk4D,iDACApD,aAAsB7rB,gCACtBgsB,cACAF,QACArkC,yDAGAsnC,iCACAv6C,uBACAzd,iCACAk4D,qCACApD,aAAsB7rB,gCACtBgsB,cACAF,QACArkC,yDAGAqkC,4BACAt3C,uBACAq3C,aAAsB7rB,OAAQuwB,YAC9B9oC,2DAEAsnC,kCACAv6C,wBACAzd,4BACAga,yCACA86C,aAAsB2E,qBACtBpyC,SACAqyC,2BACA,sCACA,GACAC,8BACA,kCACA,GACAloC,6BACA,kCACA,GACA2jB,iCACA,aACA,IAEAn7B,qCACAg7C,cACAF,QACArkC,gDAIAsnC,gDACAv6C,uCACAzd,qCACAk4D,oDACApD,aAAsBpjB,mBACtBujB,cACAF,YAEAiD,wCACAv6C,+BACAzd,qCACAk4D,4CACApD,aAAsBpjB,mBACtBujB,cACAF,YAOAiD,mBACAv6C,cACAzd,cACAga,yBACAC,4BACA66C,aAAsB7rB,uBACtBgsB,gBAEA+C,2BACAv6C,gBACAzd,qCACAk4D,kCACApD,aAAsB7rB,uBACtBgsB,cACAF,QACArkC,wEAGAsnC,4BACAv6C,kBACAzd,4GACAS,QACAm5D,cAEA1B,uCACApD,aAAsB7rB,uBACtBgsB,cACAF,YAEAiD,2BACAv6C,yBACAzd,oCACAS,QACAo5D,cAEA3B,sCACApD,aAAsB7rB,uBACtBgsB,cACAF,YAEAiD,iCACAv6C,6BACAzd,qFACAk4D,qCACApD,aAAsB7rB,uBACtBgsB,cACAF,YAEAiD,gCACAv6C,4BACAzd,yBACAk4D,uCACApD,aAAsB7rB,uBACtBgsB,cACAF,QACArkC,qEAGAsnC,+BACAv6C,0BACAzd,kCACAk4D,wCACApD,aAAsB7rB,uBACtBgsB,cACAF,YAEAiD,6BACAv6C,yBACAzd,uCACAk4D,uCACApD,aAAsB7rB,uBACtBgsB,cACAF,YAEAiD,6BACAv6C,yBACAzd,uCACAk4D,uCACApD,aAAsB7rB,uBACtBgsB,cACAF,YAEAiD,+BACAv6C,0BACAzd,8BACAk4D,yCACAz3D,QACAq5D,gBAEAhF,aAAsB7rB,uBACtBgsB,cACAF,YAEAiD,+BACAv6C,0BACAzd,+FACAk4D,yCACApD,aAAsB7rB,uBACtBgsB,cACAF,YAEAiD,gCACAv6C,mCACAzd,mFACAk4D,0CACApD,aAAsB7rB,uBACtBgsB,cACAF,YAEAiD,kCACAv6C,qCACAzd,mHACAk4D,4CACAz3D,QACAo5D,cAEA/E,aAAsB7rB,uBACtBgsB,cACAF,YAEAiD,iCACAv6C,4BACAzd,mGACAk4D,2CACApD,aAAsB7rB,uBACtBgsB,cACAF,YAEAiD,iCACAv6C,8BACAzd,yKACAk4D,6CACApD,aAAsB7rB,uBACtBgsB,cACAF,YAEAiD,8BACAv6C,yBACAzd,sBACAk4D,uCACApD,aAAsB7rB,uBACtBgsB,cACAF,YAMAiD,gBACAv6C,YACAzd,yBACAi1D,cACAj7C,uBACAC,0BACAxZ,QACAsmB,qBAGAixC,wBACAv6C,eACAzd,mCACAga,oCACAk+C,iCACAjD,cAEAF,QACAgF,cAEA9/C,kCAEA+9C,qBACAv6C,aACAzd,+CACAotB,+KACAnT,2EACA0I,+BACAzI,QACA3L,sDACA2L,cACAA,eACiB,EAEjB,GACA+6C,cACAF,QACAgF,gBAGA/B,qBACAv6C,aACAzd,YACAk4D,qCACAjD,cACAF,QACAgF,gBAGA/B,wBACAv6C,qBACAzd,oBACA80D,aAAsB7zB,eAAgB9hC,WACtC6a,gCACAC,kCACAg7C,cACAF,YAEAiD,sBACAv6C,mBACAzd,kBACA80D,aAAsB7zB,eAAgB9hC,WACtC6a,gCACAC,4BAOAg7C,cACA+E,kBACAv5D,QACAyR,MACAvE,aACAy9C,WAGA2J,QACAgF,gBAGA/B,6BACAv6C,sBACAzd,+BACA80D,aAAsB38C,WAAaC,aACnC4B,qCACAC,sCACAg7C,cACAF,QACAgF,gBAWA/B,oCACAv6C,sBACAzd,+BACA80D,aAAsB38C,WAAaC,aACnC8/C,qCACAjD,cACAF,YAEAiD,sBACAv6C,cACAzd,+EACAk4D,gCACAjD,cACAF,QACAgF,gBAGA/B,wBACAv6C,yBACAzd,0BACAk4D,uCACAjD,cACAF,QACAgF,gBAGA/B,kCACAv6C,8BACAzd,+BACAk4D,+BACApD,aAAsBrpB,eACtBwpB,cACAF,YAEAiD,oBACAv6C,yBACAzd,6BACAk4D,0DACAjD,cACAF,QACAgF,gBAGA/B,sBACAh4D,+FACAk4D,mCACAjD,cACAF,YAEAiD,uBACAh4D,wBACAk4D,iCACAjD,cACAF,YAEAiD,yBACAh4D,gCACAk4D,mCACAjD,cACAF,YAEAiD,6BACAh4D,oBACAk4D,sCACAjD,cACAF,YAEAiD,6BACAh4D,wCACAk4D,4CACAjD,cACAF,YAEAiD,6BACAv6C,qBACAzd,6EACAk4D,mCACAjD,cACAF,YAEAiD,4BACAv6C,oBACAzd,mBACAk4D,qCACAjD,cACAF,YAKAiD,gBACAh4D,WACAi1D,cACAj7C,uBACAC,4BAEA+9C,uBACAh4D,8CACAk4D,iCACAjD,cACAF,YAeAiD,wBACAh4D,mBACAyd,oBACAw3C,cACAj7C,+BACAC,kCACA86C,QACAkD,mBAGAD,+BACAv6C,2BACAzd,4FACAk4D,6CACAjD,cACAH,aAAsB7zB,mBACtB8zB,QACAe,oEACA97C,iEACAi+C,gBACAW,eAGAZ,+BACAv6C,2BACAzd,0BACAk4D,mCACApD,aAAsB7zB,mBACtBg0B,cACAF,YAEAiD,kCACAh4D,oCACAk4D,wCACAjD,cACAF,YAEAiD,6BACAh4D,2CACAk4D,qCACAjD,cACAF,YAKAiD,kBACAv6C,cACAzd,iFACAk4D,uCACAjD,cACAF,YAEAA,sBACAt3C,0BACAq3C,aAAsB7zB,qBAKtB+2B,kBACAv6C,cACAzd,aACAi1D,cACA1L,SACAwL,QACA3nC,uFACA6sC,wCAGAjC,yBACAv6C,qBACAzd,oBACAk4D,4BACAjD,cACAH,aAAsBoF,iBACtBjgD,iCACAC,wBACA,GACA66C,QACAkF,qCACA9B,qBAAoC,yCAGpCpD,yBACAt3C,uBACAq4C,oEACA97C,gEACA4+C,YACAX,kBAEAlD,yBACAt3C,uBACAq4C,oEACA97C,iEACA4+C,YACAuB,kBAEApF,uBACAt3C,eACAq3C,aAAsBrpB,eACtBwuB,qCACA7sC,wFAIA,GAGA,CAh2DA,GCCA,WACA,aAEAruB,wFACA,gBAIA8tB,iBACA7sB,iBACAga,oDACAC,uBACA/B,MACAs9C,2BAEAnuC,SACA+yC,qBAAoC,kBACpCC,sBAAqC,qBACrCC,+CACA,mCAA6DniD,WAAYC,aAAa,MAAS4vB,oBAC/F,MAEK0kB,sBACL1sD,cACAu6D,YACAlzC,SACAmzC,oDAiBA,cACA,CAA0BC,eAhB1B,CACA,0BACA,wCACA,kCACA,sBACA,+BACA,yBACA,2BACA,wBACA,4BACA,iCACA,2BACA,qBAI0Bv7D,gBAA2C,aACrEwP,gBACA,kCACA,EACA,IAEAsL,iCACA9B,MACAs9C,iCACAziD,iBAEAkH,iCAEAC,yBACAA,iBACAA,0BACAA,4BACAA,yBACA,EAEA,KAGAwyC,2BACA1sD,mBACA06D,OACA,qBACA1gD,yCACAC,2BAEA0gD,WACA3gD,6CACAC,8BAIAyyC,6BAEA1sD,oBACA06D,OACAE,MACA5gD,sDAEA2gD,WACA3gD,6CACAC,2BAEA,8BACAD,2CACAC,mCAEA,gCACAD,6CACAC,yCAEA,4BACAD,yCACAC,uBAEAoP,MACArP,yCACAC,wBAGA/B,MACA2iD,WAEAp6D,QACA+hC,WAEAnb,SACAyzC,qCAA+D,kBAC/D9uC,iBAA+B,aAG/B0gC,uCACA1sD,4BACA06D,OACA,kCACA1gD,2CACAC,mCAEA,oCACAD,6CACAC,0CAGAoN,SACAyzC,qCAA+D,qBAC/D9uC,iBAA+B,WAE/B9T,MACA2iD,aAIAnO,+BACA1sD,cACAga,qDACAC,2BACA/B,MACA6iD,gBACAvF,uCAEAnuC,SACAizC,+CACA,yDAAqFtyB,oBACrF,MAGA0kB,gBACA1sD,kBACAg7D,qCAGAtO,6BACA1sD,eACAga,4DACAC,8BACAoN,SACAoK,6BACA,kCACA,MAIAi7B,4BACA1sD,yBACAga,6DACAC,oCACAoN,SACAmK,6CAEA,qBAAyC1xB,iBAA2BgoB,QACpE,MAGA4kC,oCACA1sD,iCACAga,8DACAC,qCACAoN,SACAmK,6CAEA,qBAAyC1xB,iBAA2BgoB,QACpE,GACA6J,8CACA,4BAAgD7xB,iBAA2BgoB,QAC3E,GACAmzC,wDACA,qCAAyDn7D,iBAA2BgoB,QACpF,MAGA4kC,mCACA1sD,2CACAga,oEACAC,0CACAoN,SACAmK,6CAEA,qBAAyC1xB,iBAA2BgoB,QACpE,GACAozC,6CACA,qBAAyCp7D,iBAA2BgoB,QACpE,GACAmzC,wDACA,qCAAyDn7D,iBAA2BgoB,QACpF,MAKA4kC,0BACA1sD,YACAga,yDACAC,sCAGAyyC,iCACA1sD,cACAga,gEACAC,sCACAoN,SACA8zC,gCACA,yCACA,GACAC,iCACA,qCACA,MAGA1O,mCACA1sD,gBACAga,kEACAC,0CAGAyyC,iCACA1sD,kBACAga,gEACAC,sCACAoN,SACAg0C,6CACA,cACA33C,kBAAiD5jB,UAAoBgoB,SAGrE,CAA4BstC,UAE5B,MAGA1I,gCACA1sD,aACAga,+DACAC,uCAEAyyC,uCACA1sD,oBACAga,sEACAC,6CAGAyyC,sCACA1sD,uBACAga,qEACAC,4CAGAyyC,mCACA1sD,gBACAga,kEACAC,0CAGAyyC,kCACA1sD,mBACAga,iEACAC,uCACAoN,SACAi0C,8CACA,cACA53C,mBAAkD5jB,UAAoBgoB,SAGtE,CAA4BstC,UAE5B,GACAmG,gDACA,cACA73C,wBAAuD5jB,UAAoBgoB,SAG3E,CAA4BstC,UAE5B,MAIA1I,0BACA1sD,YACAga,yDACAC,sCAEAyyC,mCACA1sD,gBACAga,kEACAC,0CAEAyyC,iCACA1sD,kBACAga,gEACAC,sCACAoN,SACAm0C,iDACA,cACA93C,kBAAiD5jB,UAAoBgoB,SAGrE,CAA4BstC,UAE5B,MAKA1I,uBACA1sD,eACAga,sDACAC,gCAGAyyC,6BACA1sD,0BACAga,4DACAC,mCACAoN,SACAqqC,kDACA,4BAAgDj0C,6CAAwEqK,QACxH,MAIA4kC,kCACA1sD,mCACAga,iEACAC,wCACAoN,SACA2qC,wDACA,YACAtuC,kBAAiD5jB,UAAoBgoB,SAGrExZ,UAEA,GACAmtD,2DACA,YACA/3C,+BAA8D5jB,UAAoBgoB,SAGlFxZ,UAEA,GACAyjD,4DACA,YACAruC,gCAA+D5jB,UAAoBgoB,SAGnFxZ,UAEA,MAGAo+C,oCACA1sD,+BACAga,mEACAC,0CACAoN,SACAq0C,0DACA,YACAh4C,oBAAmD5jB,UAAoBgoB,SAGvExZ,UAEA,GACAqtD,6DACA,YACAj4C,mCAAkE5jB,UAAoBgoB,SAGtFxZ,UAEA,GACAqrD,8BACA,kCACA,MAIAjN,+BACA1sD,oBACAga,2EACAC,iDACAoN,SACAo0C,2DACA,IACAziD,EADAgqB,iBACA9jC,gBACA,2BAAmDY,OAAOgoB,QAC1D,GAEA,eACA,GACA6zC,6DACA,IACA3iD,EADAgqB,iBACA9jC,gBACA,0CAAkEY,0BAA4BgoB,QAC9F,GAEA,eACA,MAIA4kC,+BACA1sD,mBACAga,8DACAC,qCACAoN,SACAu0C,2DACA,4BAAgD97D,UAAoBgoB,QACpE,GACA6zC,6DACA,2CAA+D77D,UAAoBgoB,QACnF,GACA6xC,8BACA,kCACA,MAIAjN,8BACA1sD,cACAga,6DACAC,oCACAoN,SACAsyC,8BACA,kCACA,MAIAjN,+BACA1sD,aACAga,8DACAC,qCACAoN,SACAsyC,8BACA,kCACA,MAIAjN,0CACA1sD,0BACAga,yEACAC,+CACAoN,SACAw0C,+BACA,+CACA,MAGAnP,4CACA1sD,4BACAga,2EACAC,iDACAoN,SACAw0C,+BACA,kDACA,MAGAnP,0CACA1sD,0BACAga,yEACAC,+CACAoN,SACAw0C,+BACA,gDACA,MAGAnP,+BACA1sD,eACAga,8DACAC,qCACAoN,SACAsyC,8BACA,kCACA,MAMAjN,yBACA1sD,WACAga,kDACAC,4BAGAyyC,+BACA1sD,aACAga,wDACAC,+BACAoN,SACAy0C,gDACA,yBAA6Cp7D,uDAA4DonB,QACzG,GACA6xC,8BACA,kCACA,MAIAjN,8BACA1sD,gBACAga,0DACAC,iCACAoN,SACA00C,+CACA,uBAA2Cj8D,UAAoBgoB,QAC/D,GACAk0C,sDACA,8BAAkDt7D,2EAA+FonB,QACjJ,MAMA4kC,yBACA1sD,WACAotB,+BACAnT,4BAEAyyC,+BACA1sD,yBACAga,8DACAC,+BACAoN,SACAw0C,gDACA,wCACA,GACAlC,8BACA,kCACA,MAGAjN,8BACA1sD,gBACAga,gEACAC,iCACAoN,SACA+F,+CACA,8BAAkDttB,UAAoBgoB,QACtE,MAIA4kC,qCACA1sD,uBACAga,oEACAC,yCAGAyyC,6CACA1sD,eACAga,iFACAC,0CACAoN,SACA40C,mCACA,0DACA,6BACAhsD,8DACA,GACA8I,CACA,EACA,MAGA2zC,gDACA1sD,kBACAga,oFACAC,6CACAoN,SACA60C,sCACA,kCAAsDx7D,eAAconB,QACpE,MAGA4kC,4CACA1sD,kBACAga,kFACAC,yCACAoN,SACA80C,mDACA,mBACA,CAA4BC,mCAAsCC,gBAGlE34C,oBAAmD5jB,UAAoBgoB,0BACvE,wBACA/O,wCACA,iCACAA,iBAEA,GACAA,CACA,EAEA,GACAmjD,sCACA,2CACA,GACAzqC,6BACA,kCACA,MAGAi7B,iDACA1sD,uBACAga,gFACAC,6CACAoN,SACAi1C,uDACA,+BAAmDx8D,UAAoBgoB,QACvE,MAIA4kC,2BACA1sD,WACAotB,+BACAnT,mCAGAyyC,iCACA1sD,aACAga,4DACAC,kCACAoN,SACAqxC,6BACA,gCACA,GACA6D,iCACA,mCACA,MAGA7P,+BACA1sD,WACAga,8DACAC,kCAEAyyC,kCACA1sD,kBACAga,8DACAC,oCACAoN,SACAm1C,6CACA,qBAAyC18D,UAAoBgoB,QAC7D,GACA20C,2CACA,yBAA6C38D,UAAoBgoB,QACjE,MAGA4kC,gCACA1sD,4BACAga,kEACAqN,SACAm1C,6CACA,qBAAyC18D,UAAoBgoB,QAC7D,GACA40C,0CACA,wBAA4C58D,qCAAgEgoB,QAC5G,GACA60C,8CACA,wBAA4C78D,oBAA2CgoB,QACvF,IAEA7N,yCAKAyyC,0BACA1sD,YACAotB,+BACAnT,2BACAoN,SACAu1C,kDACA,sDACA,4BAAmD,kBACnD,EACA,MAIAlQ,gCACA1sD,aACAga,+DACAC,gCACAoN,SACAw1C,+CACA,iCACA,MAGAnQ,+BACA1sD,gBACAga,iEACAC,kCACAoN,SACAy1C,qDACA,cACAp5C,eAA8C5jB,UAAoBgoB,SAGlExZ,QACAyuD,iBACArsC,eACA0kC,QACA4H,iBACArxD,yBACAsxD,eACAC,gBAGA,GACAC,yDACA,cACAz5C,mBAAkD5jB,UAAoBgoB,SAGtExZ,UAEA,MAGAo+C,+BACA1sD,qBACAga,qEACAqN,SACAy1C,8CACA,sBAA0Ch9D,UAAoBgoB,QAC9D,GACA40C,0CACA,yBAA6C58D,oBAA2CgoB,QACxF,IAEA7N,uCAGAyyC,2BACA1sD,aACAotB,+BACAnT,4BACAoN,SACA+1C,kCACA,8CACA,MAGA1Q,iCACA1sD,aACAga,gEACAC,iCACAoN,SACAg2C,4CACA,wDACA,cAAyCp0B,OAAQuwB,iBAAehrD,OAAsBy6B,OAAQuwB,iBAC9F91C,4BAA+D45C,iBAA0Bx1C,SAGzF,EAEA,EACA,MAGA4kC,gCACA1sD,gBACAga,+DACAC,gCACAoN,SACAmyC,4CACA,iCAAqD15D,UAAoBgoB,QACzE,MAIA4kC,yBACA1sD,WACAga,wDACAC,4BAGAyyC,+BACA1sD,aACAga,8DACAC,+BACAoN,SACAk2C,+CACA,sCAA0D78D,uDAA8DonB,QACxH,MAIA4kC,8BACA1sD,gBACAga,gEACAC,iCACAoN,SACAm2C,8CACA,cACA95C,6BAA4D5jB,UAAoBgoB,SAGhF,EAEA,MAGA4kC,kCACA1sD,oBACAga,iEACAC,mCAIA,IAGAlb,yDAEAC,0FAGA,OACAooB,wBAaA,OAXAtN,QACAC,YACAC,kEACAC,uDACAC,2BACAC,UACA,CACA,GACAC,wCACAC,YAEAvB,MAEA,EACA2kD,0BA+CA,OA9CA3jD,QACAC,YACAC,qEACAC,WACA,4CACAC,kBACA,OACAnI,0BACAiV,8BAEAxnB,iCACAqQ,8CACA,GACAqK,yBAEAA,gDAA+F,sBAC/FA,+BACAlW,SAAuCw0C,4BACvCn4B,mBAAgD,sCAEhDnG,wBACAA,mCACA3J,EACA,EACA2J,kBACA,SAEAnb,qCACAS,8BAA4E,wCAG5E,YACAT,gCACA03C,WACA,GAEAn7B,YAEA,GAEAnB,UACA,CACA,GACAC,sCACAC,YAEAvB,MACA,EAGA,IAEAmB,uSACAmkB,gBACAzb,aACA,iBACAzI,sCACAA,eACA/G,aACAQ,cACA,EAAS,KAGTuG,qCACAA,iBACA,CACA,gGACA,uBACA,2DACA,CACA,sFACA,uGACA,GAGA,EAEAA,gCACA,SACAnb,4CACAA,8BACA2+D,OACA,EACA,GACAxjD,+CAAyE,aACzE,EAEA1L,kCACA0L,kDAGAA,4BAEAA,uBACAA,iDAGAA,mBACAA,uCACAuY,8CACAvY,mBAEA,GAAK,GAELA,0BACAA,iDACAA,kBACA,EAEAA,wBACA1L,YAA8BmvD,qCAAyCjvD,gBACvEwL,mBACAA,gDACA,EACA,EAEAA,yBACAA,4BACAA,mBACAA,uBACAA,gDACA,EAEAA,WACAA,SACAA,gCACAA,iBAEAA,yBAMA,QALA66C,gBACAvmD,sBAIAumD,cACAA,cAIA,EAGA76C,6BACA,IAEArL,EAFA0D,wBACAwG,KAEA,QAAkBlK,IAAQA,IAC1BkK,6BAEA,QACA,EAGAmB,mBACAqtB,2BACAq2B,8BAGA1jD,6BACAA,8BACAmB,UAEA,EAEAnB,eACA3L,8BACAlO,WACKqO,iBACL,aACAytB,KACA,eACAA,QACA1e,iBACA2e,8BACAC,oBACAC,4BAGApiB,iCAAoD,wBACpD,GAEA,kBAEA,MADA,oCAQA,0EAsBiB,GAajB,+BAEA,qDACAa,iDAGA,4BAEA,wBACA,0BACA,kBACAyM,4BAEAA,2BAEA,CACArU,aACA/T,aACA,OAAkD6kB,iBAClD2rB,KAEA/3B,oBAEA,EACA,SAAU,yBACV1E,aACA0E,6BACA,QAAa,GACH,4BACV8K,mCAEA,6BACA9K,kDAEA,+BAEA0xC,cADAnqD,2CACAA,uBACAA,gBACAmqD,mBACAluC,UACA,QAEA,GACA,+BACA,8CACA,WACA,OAEA,wGACA,cACA,mBAEA1H,YACA,2CACA,iIACA,MAEAkM,4JAEA,SACA,iCACA1M,aACA,wBACAxK,GACAkP,4CAEA,QAAa,GAEb,+BACA,IAEAwU,EAFAlP,YACA61B,aAEArqC,oBACA,gBAGiB,IAGjBA,kCACA,oCACAwK,aACA/E,OAA0CC,4DAC1C,IAGA1F,SACA0jB,WACA+R,wBACA/R,2BACAhR,0BAA0D0L,yCAC1D,IAEA,GACcpe,gCACd,2DACAwK,aACA/E,OAA0CC,2DAC1C,IAGA1F,SACA0jB,WACA+R,wBACA/R,2BACAhR,0BAA0D0L,2BAC1D,IAIA,0BACAw3B,GACAprC,aACAkI,0BAAwD0L,gBAA0B,CAAGyb,WAErF,EAEA,MA3CArvB,aACA/E,OAAsCC,2EAAuCuJ,aAC7E,EA0CA,KAAU,wCAEV1J,gBAEAq7C,sCACA,MACAr7C,eACA,OACA4a,8BACAkM,8BACA71B,QAEAC,SACAy+D,0CACA,CACA,SAhKA,GAPA3vD,mFAEAgM,eACA/G,aACA+G,gBACA,GAEA,8BACA,8DACA,OAEA,8DACA,OAEA9a,6CACA8O,4CACA4vD,2CACA,KACA,8BACA3qD,aACA0E,gCACA,EAsJA,EAEAlE,yBACAA,qCACAuG,4BACAvG,uCACA,KAEAA,kCACAuG,4BACAvG,iCACA,GAGA,IAEAsG,qEACAC,gCACAA,yCACAA,0CAEAA,sCACAA,+CACAA,oDAEAA,2BACA,iCACA,CACA,IAEAD,oFACAC,uBACAA,8BACAA,uCACAA,yCACA,IAEAD,uGACAC,kBACAA,sCACAA,+CACAA,yCACA,IAEAD,wFACAC,kCACAA,2CACAA,yCACA,IAEAD,4GACAC,4BACAA,iCACAA,0CACAA,0CACAA,uBAAuCpa,eACvC,IAEAma,8FACAC,gCACAA,yCACAA,uCACA1L,kDACA0L,6CACA,EACA,IAEAD,uEACAC,kCACAA,2CACAA,wCACA,IAEAD,2EACAC,+BACAA,wCACAA,qCACA,IAEAD,mFACAC,sBACA6jD,mCACA7jD,gBACAA,kCACApM,wCACA,mBACAkwD,0FACA/yC,0CACAgzC,qBACAhzC,yCACA/rB,kBAEA4O,YACA,EACA,EACA,EAEAoM,wBACA6jD,kCACA7jD,YACA,EACA,EAEAA,yBACA6jD,mCACA7jD,aACA,mDACA2Y,sDACA,EACA,EAEA3Y,YACA,IAEAD,2GACA,QACAC,eACAA,gBACAA,gBAEAA,yBACAA,+BACAgkD,uBACA,EAEAhkD,6BACAuY,GACAyrC,WAEA,GAAK,GAELhkD,sBACA,qCACA,EAGAA,YAEAA,wBACA,gCACA,EAEAA,qBACAJ,QACAC,YACAC,2EACAC,0CACAG,oBACAiN,SACA82C,iBAAoC,UACpCC,iCACA,kBACA,KAGA,CACA,IAEAnkD,kJACAC,UACAA,eACAA,UAEAA,sBACA2X,WACAC,gBACAC,mBACA8U,oCACA1lC,UACAmjC,QACAnlC,mBACA,SACA,kEADA,EAEA,EACA2d,qBACA,SACA,kEADA,EAEA,IAIA5C,uBACAA,qBACA,EAEAA,wBACAA,kBACA,EAEAA,sBACA,SACA,2BAEAA,8BACAhF,aACAu+B,KACA,GACAA,EACA,EAEAv5B,QAAmB+I,SAEnB/I,mBACA,gCAA0D,oBAC1DmkD,2CACAlkD,UAEA/L,MADA,gBACA,CAAkCC,0EAAiCuJ,aAEnE,CAAkCvJ,uCAClC,EAAS,YACTD,2CACA+L,SACA,EACA,CACA,IAEAF,8JACAsS,KACArS,cAA6B+J,MAC7B/J,wBACA,wEACA,iCACA+pB,6BACAhwB,+BACA,CACAiG,kBACAkkB,sBAEA,EAEAlkB,uCACAmB,6BACAlI,aACA,wBACA,MACA,UACA,2BACA8Q,4BAEA/J,gBACA,CACA,GAEAA,iBAEA,GACAA,iCACAA,iBACA,GAEAA,0BACA,aAA0C+J,mBAC1Cma,mBACA,CACA,IAEAnkB,+HACAC,aACAA,UACAvZ,YACAC,WACA09D,iBAEApkD,0CACAA,gCACA,UACAqkD,OACArkD,cACAA,SAEAA,0BACA,cACAyb,MAEAzb,WADAA,SACAqkD,aAGArkD,oBAEA,EAEAA,0BACAqkD,+BAEA,GADA,6BACAr3D,2BACA,GACAgT,eAAkCsQ,8BAGlCtQ,mBAFA,EACAskD,IAEA,EAEAhwD,+FACA0L,2BACAA,oCACAvZ,+BACAuZ,kBACAA,iBAEA,EACA,EAAK,YACLhM,2EACA,GAEAgM,0BACApC,QACAoC,SACAwJ,oBAAgC/iB,wDAChC,YACAuZ,aACApM,sBACAoM,gBAAyC6M,4FACzC,GACA7M,SACAA,iBAGAA,eAA8CsQ,sC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EAhZ,oBACAA,eACAA,qCACAA,iCACAA,4BACA,KAGA7L,eACAA,2DACAA,sDAEAsF,oBACAiE,wBACAkpB,gBACA5oB,MACAmnD,wBACAvrB,gCAEiB,KAIjB,GAjCA,GAmCA55B,iCACA,MACA,SACAklD,sBACAlgE,aACA,GACAgb,mCACA,CACA,GAEAA,sBACAA,gBACAA,cACA,EAEAA,yBAEAA,UACAgpB,UACAo8B,cACAC,UACAC,aAEA,SACA1nD,iCACA,8CACA2I,IACA3I,qCACA1J,OACAC,oEACA,iCACAuJ,iBAKAsC,eArFA,aACAhM,6BACAyF,oBACAiE,wBACAkpB,iBACS,IACT,CAgFA2+B,GACAC,IACA,GAEAxlD,gCACAwJ,wBACAkkC,eACAhV,oBACAoU,SACa,YACb94C,eACAgM,wBACAylD,oCACAA,eACAzlD,oEACAA,qBACA,EAAa,YACbhM,gBACA,qCACA,wCACAgM,sBACAM,gBACArb,YACA6nD,QACAzsC,UAEAL,oEACAA,qBACA,EAEA,EAEA,EAEAA,sBACA,gEACAA,oCACA4zB,mDACA,GACAn6B,2CAAmCisD,UACnC,EACA1lD,uBACA,yDACAA,oCACA4zB,sCACA,GACAn6B,2CAAmCisD,UACnC,EAcA1lD,UAZA,aACA,MACA,+CACAla,yEACU2T,cAIV,YADAkM,2DAFA7f,8EAIA,CACA2T,uBACA,CAIA,IAIAsG,8IAEAC,KACA2lD,aACAxwB,MACA9kB,QACAC,SACA6kB,QAEA/S,OACA1mB,qBACAvK,YAEAy0D,eAGA5lD,mBAAuCw+C,mBAEvClqD,uCACA0L,qDAGAA,gBACA,CAAUgP,iCACV,CAAUA,gCAGVhP,qBACA,CAAUgP,oCACV,CAAUA,kCAGVhP,4BACApC,QACA4L,wBAAoC5jB,UAAcgoB,0BAClDhQ,aAEA0a,SACA1Y,QACAE,iEACAI,oBACAH,iCACAC,eACAA,UACAA,mBACAA,gBACA,EACAA,oBACAA,gBACA,CACA,KACiBpB,OAGjBxK,YAGAI,sBACA,QAEAoJ,QAEA4L,SADA/X,wBACA,CACA7L,SACiB,CACjBigE,8BACiBj4C,0BACjBhQ,YACA1J,OAAsCC,qFAA2DuJ,cACjGyD,UACA,EAAiB,YACjBvD,YACA1J,+DACAiN,UACA,GACA,EACA,EAAS,YACTvD,YACA1J,kDACA,EACA,EAEA8L,6BACAJ,QACAE,+DACAK,UACAJ,iCAEA,GADAC,QAA+BtC,iCAC/B4kD,cAAwCwD,UACxC,gCACA9lD,qBACAA,iDAEAsiD,mCACAtiD,iBACA,mDACAA,mBACAA,kBACA,CAGAA,qBAEA,GADAA,SACA,wBACA,gBACA,2CACA,UAGA,oBACA,kBACA,0CACA,EAEA,kBACA,0CACA,EAEA,2CACA,4CACA,CAEA,CACA,uBACAA,mBACAA,2CACA,GAIA,CACA,KACSpB,wBACT,MACA,IAOAyoB,EAPAjP,KAQA,MAPA,kBACAA,cAGAA,yDAGA,cACAiP,eAEArnB,kBACA,CACA,EACA,CACA,IAEAD,qKAEAC,KACA2lD,aACAxwB,MACA9kB,QACAC,SACA6kB,QAEA/S,OACA1mB,qBACAvK,YAEAy0D,eAEA,iCACAG,cACA/lD,sBACAA,YACAA,8BACA3H,iCACAi4C,mCACA,GACAj4C,+CACA,gBACA,EACA,GACA2H,4BACA,qBACA,GAEAA,cACAA,oBACAA,kBAEAA,sBAEA,MACA4lD,iBAEAI,EADAJ,SACApH,qBACA,UACAzsC,uCACAk0C,OACA,GACA,YAA8BtxD,WAAqBA,IACnD,YACA,SAEA,QACA,GAGA6pD,EAGAx+C,yBAEAgmD,qBACA,sBACA,yBACA,SACc,yBACd,SAEA,6BACA,8CACA3tD,EAEA5B,sCACA4B,EAGA,CAEA,GACA2tD,gBACAA,wDACAA,EAEAhmD,gCAEAkmD,IACAlmD,cACA,EAEAA,yBACAA,gDACAA,WACA,EAEAA,yBACAA,iBACAA,gBACAA,WACA,EAEAA,gCACAuY,GACAvY,eACA,GAAK,GAELA,yBACAA,iBACAA,gBACAA,aACA,EAEAA,gBAEA1L,iCADA,CAAqB6xD,kDAErBjyD,OAA0BC,sCAAwC,EAGlE6L,mBACA,4CACAxZ,yCACAwZ,oBAEAxZ,wCAGAwZ,2BACAA,6BACAA,uBACAA,gBAEA,EAEAA,8BACAA,iDAAuE,cACvEA,eACA,EAEAA,8BACAA,mBACA,EAEAA,+BAEA,MADApC,QAEAwoD,EACAt/D,aAGAA,eACAkZ,8BACA,iDACA,8BACA+R,+CACAjrB,YAEA,CACA,IAGA,iDAAoE,uBACpE0iB,iBAA6BoF,uBAA+BhB,yBAC5DhQ,YACAoC,UACA,EAAS,YACTpC,YACA1J,uCACA,GACA8L,sCACA,EAEAA,4BACA,QACAJ,QACAE,8DACAI,oBACAH,iCACAC,sCACAA,UAAiCuC,SACjCvC,oBACAqmD,uBACArmD,UACA,EACAA,oBACAA,UACA,CACA,IAEA,EAEAA,4BACApC,QACA,iDAAoE,uBACpE4L,iBAA6BoF,4BAA+BhB,yBAC5DhQ,YACAoC,UACA,EAAS,YACTpC,YACA1J,uCACA,GACA8L,sCACA,EAEAA,yBACAglB,UACAvmB,wBACAC,gFACAC,UAAuB1I,2CAAiD,CAAGA,0BAClE2I,wBACT,KAEA,SACA,iDAAwE,uBACxEoB,aACAwJ,iBAAiCoF,wBAA2BhB,yBAC5DhQ,YACAoC,UACA,EAAa,YACbpC,YACA1J,uCACA,EAAa,CACb,EACA,EAEA8L,wBACAA,gBACAA,WACA,EAGAA,kBACAA,gCACA,QACAnb,gCACAoR,MACA,GACA+J,iBACA,GAAK,GAELA,oBACApC,QACA4L,2CACA,qBACAxJ,8BACA3H,iCACAi4C,mCACA,GACAj4C,+CACA,gBACA,EACA,GACAmR,4BACA,GAAShV,iBACTwL,8BACA,qBACA,GACApC,YACAoC,WACA,EAAS,YACTpC,YACA1J,yCACA8L,WACA,EACA,EAEAA,gBACAA,cAEAA,kBACA+R,eACA/R,cACA,EAEAA,wCACAnb,sCACAktB,oBAEAA,YADA,eACA,cAEA,gBAEA,EACA,EACA,IAEAhS,4IACAC,SACAtE,qBACAvK,YAGA6O,2BACApC,QACA,0BACAoC,sCAGAwJ,cAA0B5jB,gBAAqBoa,mCAC/CpC,YACA1J,OAA8BC,mCAC9B6L,iBACA,EAAS,YACTpC,YACAoC,aACA9L,+BACA8L,iBACA,EACA,EAEAA,2BACApC,QACA4L,cAA0B5jB,iBAAqBgoB,0BAC/ChQ,YACA0kD,IACAtiD,kBACA,EAAS,YACTpC,YACA1J,gCACA,EACA,EAEA8L,4BACAA,WAGAA,YADA,iBACAA,yBAGA,CACA4wC,OACA0V,OACArwD,OACAswD,iBACAC,iBAGA,EACAxmD,mBAEAA,wBACA,8BAEA1L,OAAkC2gC,uBAElC3gC,gBAIA,EAEA0L,yBACApC,QACA4L,kBAA8B5jB,iBAAqBgoB,0BACnDhQ,YACA2kD,IACAviD,gBACA,EAAS,YACTpC,YACA1J,gCACA,EACA,EAEA8L,0BACA,sBACAymD,qBACA,2BACA,0CACApuD,EAEA5B,kCACA4B,EAGA,CAEA,GACA2H,QACA,EAEAA,6BACAuY,GACAvY,gBAEA,GAAK,GAELA,iBAEAA,yBACApC,QACA4L,oBAAgC5jB,mBAAyBgoB,yBACzD,yBAAyChoB,UAAcgoB,QACvD,GAASpZ,iBACT+tD,IACAviD,iBACApC,WACA,EAAS,YACTA,YACA1J,iCACA8L,YACA,EACA,EAEAA,wCACAk7C,mBACAl7C,eACA,GAEAA,0BACA,sDACAA,oCACA,CACA,IAEAD,kHACA,0BACAC,WACA,wBACAA,4BACA4wC,4BACA0V,4BACArwD,6BACS+J,2BAGTA,sBAGA,GAFAA,QACAA,sBACAA,mBACAA,iBACA,YAA0BrL,uBAA6BA,IACvD,qCACAqL,oBACA,YAAkC4b,qBAA2BA,IAC7DzL,gCAEA,MAEAnQ,mBAGA,MAEAA,iBACAA,qDACAA,iBACAnb,wCACAA,8BACAsrB,gBACA,EACA,GAGA,EAEAnQ,oBACApC,QACA4L,iBACA5jB,QACA8gE,SACAvgD,mBACA2yB,iCACAzoB,cACAC,gBACS1C,0BACThQ,YACAoC,YACA,EAAS,YACTpC,YACA1J,iDACA,EACA,EAGA8L,UADAA,cACAyiD,EAGAD,GAGAxiD,gDACAA,4CAEAA,UACAA,WACAA,wBAAmC+G,2DACnC/G,qBACAA,sBACAA,UACA,EACAA,SACAtE,SACAvK,YAGA6O,6BACAuY,YACAvY,UAEA,GAAK,GAGLA,4BACA,yBACAwJ,wCAAoE5F,0BACpE,EACA5D,6BACA,+CAAoE4D,eACpE,EAEA5D,2BACApC,QACA4L,2BAAuC5jB,kBAA2B,IAAIgoB,0BACtEhQ,YACA1J,OAA8BC,gHAC9B,EAAS,YACTyJ,YACA1J,+CACA,EACA,EACA8L,8BACApC,QACA4L,8BAA0C5jB,kBAA2B,IAAIgoB,0BACzEhQ,YACA1J,OAA8BC,0GAC9B,EAAS,YACTyJ,YACA1J,4CACA,EACA,CAEA,IAEA6L,wGAEAC,UACA2mD,oBACA9+D,aAGAmY,iBACAgP,iCACK,CACLA,gCAGAhP,sBACAgP,oCACK,CACLA,kCAGAhP,2BACA,0BACAA,sCAGApC,QACA4L,kDACA5L,YACA1J,OAA8BC,mCAC9BgN,iCAAmDvb,SAAW,CAAG0iC,WACjE,EAAS,YACT1qB,YACA1J,yCACA,EACA,EAEA8L,0BACA,sDACAA,oCACA,CACA,IAGAD,kFACAC,gBAEAA,0BACAJ,QACAE,0EACAI,oBACAH,iCACAC,UACA,IAEA,EAEAA,eACA4mD,iBACAC,wBACAC,mBACAC,iBACAC,kBACAC,0BACAC,qMACAC,YACA,CAAczrD,YACd,CAAcA,4CACd,CAAcA,yCACd,CAAcA,8CACd,CAAcA,8CACd,CAAcA,eACd,CAAcA,yCACd,CAAcA,iEACd,CAAcA,8BAIdrH,mCAA6CoJ,kBAAoBjJ,iBACjEwL,yBACA,GAEAA,wCACAA,cACAA,2DACA4wC,aACA5wC,gBAGAA,eAEA,GACAA,gEACAkS,sBACA0+B,aACA5wC,gBAGAA,eAEA,EACA,EACA,EAEA,iBAQA,QAPAonD,0CACAC,6DAMA1yD,IAAwBA,IAAOA,IAH/B,iBADAwJ,EAKAkpD,OAJAlpD,cAIAkpD,SALA,MAQAA,6BAEA,OAA8BxjD,0BAE9B,6BACA,kBACAyB,gDAEAgiD,gBAA0C/9C,uEAC1C,UAEA+9C,wCACAA,CACA,EAEAtnD,2BACA,UACAunD,4BACA,EAEAvnD,8BACA,UACAunD,+BACA,EAEAvnD,yBACAA,wCAEAtF,oBACA4K,yBACAjR,6CACA,GAEA2L,0CAEA3L,mCAAiDoJ,kBAAoBjJ,iBACrEwL,yBACA,EACA,CAEA,IAEA0gC,sCACA,OACAoE,kBACAl6B,uBAEA3F,8BAA2C,oCAAyC,mBAAoB,EACxG,cACAugB,wCAAkE,yBAA+BjtB,SACjGitB,4BAEAA,2BAEA,EACA,EAEA,GACAkb,wDACA,OACA5gC,6DACAmF,SACA2F,wBACAke,mBAOA7jB,oBACA,0BACA,4DACA,0CACA,EACA,EAEA,IAEAlF,4GAEA,4BACA0I,0BACAzI,QACAjY,YACAyhB,wBAAwCvG,SAAa,YACrD8oB,2BACA,EAEA,GACAtjB,cAAiCtiB,yBAA8BqO,iBAC/DwL,YACA,GACAA,UAEA,IAEAD,2KACAyJ,aAEA,OACA5K,QACAiQ,sBACA24C,gBAEA3sB,QACAhsB,2BACA24C,gBAEArjB,aACAt1B,oBACA24C,iBAmIA,cACAC,sBACAA,cACAA,uBAEA,CArIAznD,eAEAnb,gCACAmP,eACA0zD,kCACA,GACA1zD,eAEAwV,iCAAyC,YACzCnT,sBACArC,eACA,6BACAgM,6BACA,EACA,GAEAA,uCACAA,2EAEAA,2BACA,eACA,uBACA,SAEA,QACA,EACAA,yBACA,eACA,kBACA,0CACA,iBAEAhM,sEACA1O,qCACA,SAGA,QACA,EACA0a,wBACA,SACAnb,gCACA4rB,2CACA,GACApc,gEACA6C,8BACA,EAAS,YACTlD,gBACA4J,YACA1J,gBACA,EACA,EAEA8L,WACAqrB,SAGArrB,cAA0Bpa,gBAC1Boa,yBAEA,uCAAiE,oBAAjE,CAKA,6BAA4Cpa,2BAC5C,YACA,MACA,wBACA,gCACA+hE,kBACA,MACAA,mBACA,YAAoC,uBAEpC78C,2BACA,CACA9K,cACAA,qCAfA,MAFA9L,OAA8BC,4CAkB9B,EAEA6L,uBAEA,mBACAA,YACAA,iBACAA,yBACA,EAEAA,uBACAA,kBACAA,cACA,EAEAA,sBAEA,sBACAA,mCACAomD,GACApmD,4BACAA,cAEA,GAAS9K,kBACTlB,eACA,EAEA,EACAgM,sBAEA,GADAhM,eACAgM,kBACA,gBAEAglB,UAEA,GAASpmB,wBACTgpD,GACAxjB,4BACA,6BACAzvC,MACAqL,uBACA,EAAiB,YACjBhM,eACAE,6CACA,EAEA,EAEA,EAOA8L,yBACAA,yBACA,EACAA,2BAEAA,6DACA,EACAA,0BAEA,wDACA,EACAA,2BACA,iEACA6nD,uBACA,qBAA4BtvD,QAC5BsvD,0DACAC,oBAEA,EACA9nD,wBAEA,EACA,OACA+nD,SACAA,WACA/4C,SACAg5C,aACAC,eAEAvkD,MACAsL,SACA0S,QACAse,cACAmY,WAEAn6C,MACAgR,SACAg5C,aACAtmC,QACA1jB,SAEAkqD,UACAC,WACAn5C,UAEAo5C,aACAxiE,MACAyN,aA2GA,cAIA,cACA,2BACA,oBACA,mBACAg1D,gBACAA,CACA,CA7DAroD,cACA+nD,kBACArkD,uBACA1F,uBAEAgC,aA1DA,CACAhC,mCACAoqD,aACArkB,WACAl1B,qDACArP,KACA8mC,SACAz3B,0BACAnR,eAIAkmC,eACA/0B,gEACArP,KACA9D,OACAmT,qEACAnR,eAIA6lC,gBACA10B,mEACArP,KACA8oD,SACAz5C,qDACAnR,eAEA2tB,MACAxc,gDACAnR,iBAIA4lC,YACAz0B,wDACArP,QAEA4jC,SACAv0B,oCACArP,QAEAwjC,cACAn0B,gEACArP,KACA+oD,WACA15C,6DACAnR,qBAYAsC,mBACA0jD,6BACAra,uBAEArpC,+BACA,WACA,uDACA9F,2BACA,EACA8F,sCAGAynD,WADAA,WACA,KAEA,CAA8Be,wBAC9B,EACAxoD,iCACAynD,QAAqB7hE,iBACrB,EACAoa,wBAGA,SACA,6BAEA,WAEA,uBACA,kCACAqH,MACAghD,8BAEAA,gCAIA,EACAroD,yBACAtC,QACA0a,0BACA,EACApY,qBACAkxC,aACA,EAaAlxC,6BAEA,4BACA,2BAEA,WAEA,WACA,sCACA,gBAEAqoD,aACA,CAGA,IACA3nB,2EACA,OACA7F,8EACAsJ,wFACAvlC,gFACA,uEACA,qEAEA,OACAqG,OACAm/B,UAEAlxB,SACA,ygBAeAtI,qBAEA3F,iBACAA,gBAAmC41B,uBACnCsJ,2BACAskB,4BACA7pD,iCAEA,6BAGAqG,oBACAwD,gCACAxD,QACAwD,qCACAxD,WACA+7B,SAAuC1uB,YACvCvqB,MACAukC,YAGA,QAZA,8HAYAyY,IACA2jB,yBAGAC,GAFAC,sBAEAC,0BACA,yBACA,EAAyBC,GAEzBJ,GACAC,wBACAI,UACA/0D,cACA,EACA,EACA,CACA,EAEA,IACA+L,qHAIA,kBACA,uBACA,yCACApK,aACAA,kCAEAA,sBACAA,qBAEAA,gBAGAA,eADAA,UACA,mBAEAA,OAGAA,CACA,EACAqK,yBACA,yCACArK,aACAA,kCAEAA,sBACAA,aAEA,EAEAtB,kDACA/O,+BACA0a,8CACA,GAEAA,QAEAA,qBACA,CAAUgP,+BACV,CAAUA,6BACV,CAAUA,2BACV,CAAUA,yBACV,CAAUA,sBAEVhP,oBACA,CAAUgP,yBACV,CAAUA,0BACV,CAAUA,oBAIVhP,aACA,CAAUgP,uDACV,CAAUA,sDACV,CAAUA,qDACV,CAAUA,2DACV,CAAUA,2DACV,CAAUA,oDACV,CAAUA,yEAGV1pB,cACAN,gBAA2B,OAASgqB,gEACpCtU,oBAAgCsF,sBAEhCA,qBAEA,OAAuBwxB,yCACvBw3B,8BACAC,EACAjpD,qBAEAA,uBAEA,EACAA,wBACAglB,UACAvmB,wBACAC,+JACSE,wBACTC,2BACA,EACA,EACAmB,kBAEA,6BACAglB,UACAtnB,cACAuR,qBACAxQ,yBACAC,kEALA,CASA,OAAuBwqD,8CACvB70D,mDACAG,iBACAwL,gCACA9L,OAAsCC,kDACtCE,kDACAL,eACA1O,+BACA0a,8CACA,EACA,EAAiB,YACjB9L,oCACA,EAbA,CAcA,EAEA8L,0BAGAglB,UACAvmB,wBACAC,QAHAA,gHAKSE,wBACTC,GACAxK,0CAAgEvN,aAAa,CAAGyN,SAAS,qEACzFC,iBACAR,eACAgM,gCACA9L,OAA0CC,gDAC1C7O,+BACA0a,8CACA,EAAqB,YACrB9L,mDACAF,eACA,EAEA,EAEA,CAEA,IAEA+L,mHAEA0I,uCACAzI,OACA,GAEAA,0BACA0tC,GAIAr5C,4DAAgGoJ,kBAAqBjJ,iBACrHN,OAAoCwJ,0DACpCsC,gBACA,EAAa,YACbhM,iBACAE,8BACA,EAEA,CACA,IAEA6L,0KACAza,aAOAmjB,uCACAzI,OACA,GAEA,iCAAqE7Z,WAAcqO,iBACnF,oBACA83B,CACA,GA4KA,cAQAl4B,WAWAI,gBACAwL,2BAEAA,uCADAmpD,KACA,WAEA,QAGAA,aACAA,oBACAA,SACAA,4BACAnpD,yCACA,EAEA,EACA,CACA,GA9MAA,eACAA,cACAopD,YACAC,uBAAqC,0CACrC3oD,iBACAV,2BACAlM,gBACA,GAGAkM,wBACA,4CACA5I,kDACA,EAEA4I,0BACA,iBACAgpB,KACAlqB,KAYA,OAXAisB,0CAKA/F,UACAtmB,gCALAqsB,8BACA,yBACA,+BAGA,mCAEApsB,UAAiCqQ,+CACjC,CAAiCA,2DAEjCpQ,wBACA,UAMAmsB,sBACA,gBACAjsB,kBACAwtB,qCACAp4B,OAAuCwJ,kDACvC4uB,YACAxuB,WACA,EAAiB,YACjB,gBACAkrB,YAEA90B,OAA2CwJ,iDAE3CI,WACA,EAEA,GAEA1J,yBAEA,yBAUA,OATAwL,QACAC,YACAC,kEACAC,sCACAoN,SACAmf,oBAAmD,mBACnDv1B,iBAAgD,aAGhD6H,wBACA,MACA,SACA,uCACA,SACApG,mCACAjS,MACA,GACA,gBACA+iE,YACAC,kBACAtkE,8BACA+a,qBACA9L,OAAuDwJ,uDACvD,EAAiC,YACjCzY,6BACAiP,yCACA,GAAiCiB,oBACjCm0D,YACA,EACA,GACAl1D,QACA,CACA,EAEA,GAAae,mBACb6K,wBAGA,KAhEAA,yBACAA,2BACA,KA+DA,EACA,EAEAA,wBAGA,KACA,cACArX,YAEA,qDAEA,YADAqL,iCAGA4J,mBACA,qCACA4rD,oBACA,sBAAsCxiE,kBACtC,GACAoN,0BACA4L,sCACApC,uBACA6rD,sBACAC,mCACA,EACA,EACA,EAAS,YACT9rD,uBACA5J,eACA,EAAS,CACT,EAGAgM,4BACAqoD,UACA,iBACA,gBACAroD,2BACAA,+CAEAJ,QACAC,YACAC,8DACAC,+BACAoN,SACAmf,oBAA2C,mBAC3CjE,kBAAyC,eACzCshC,kBAAwC,eACxCv3C,iBAAuC,mBAE1BxT,wBACb,KA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AAqB,YACrBA,WACAr3D,2DACA,EAEA,CACA,GAAaiB,oBACbo2D,wBACA,EAGA,EA2BAvrD,+BACAlW,SAAuBw0C,4BACvBn4B,mBAAgC,sCAChCnG,kBACAokC,UACAn/B,cAGAjF,0BACA,6BAEA,GADAA,qDACA,aACA1L,8DAEAA,qCACA,aACA,qDACAK,mCACAqL,iCAAqD,YACrDA,4BACAokC,WACA,EACA,KAAc,aACdpkC,2BACAA,2BAGA,EACAA,wBAEAA,qBAEAA,wBACAA,wDAEA1a,gCAAsD,2BACtDN,gBAA4C,gBAC5C0V,2BAAkD0pC,OAElDpkC,iCACA,EAEA1L,kCACA+zB,GACAroB,6CACAA,kCAEAA,sBAEAglB,UACAvmB,8BACAC,gDACAgY,SAAuBnU,8BAJvBvC,2BAAsDuC,MAKtD,CAAsBA,gEACtB5D,UAAuB1I,4CACvB,CAAyBA,0BAChB2I,wBACT,MACA,SAEA/Z,sCACAsT,8CAGAiJ,WACA,GACApB,uBACAA,kCACAA,kCAEAA,0CACAuC,YACA2Q,WACAorB,4BAGAhqC,sDACAuK,SACAmB,yBACAA,6BAEAA,iCAA6D,YAC7DA,wBACAA,gCACA,GAGA,CACA,EACA,CAEA,IAEAD,2LACAyJ,WAEAxJ,yBACAA,WACAA,UACAA,gCACA,iEACA,GACAA,YACAA,qBAEA,IACAA,yDAKA,CAJA,MACA9a,GACA8a,sBACAhM,eACA,CAEA,IACAgM,qDAKA,CAJA,MACA9a,GACA8a,oBACAhM,eACA,CAEA,kBACAgM,aACA,SACA,mBACAuY,SACAvY,aACA2rD,gBAGA,wBACApzC,WACAvY,aACA2rD,kBAEApzC,aACAvY,aACA2rD,oBAEA3rD,qCACAqW,wCACArW,aACA2rD,gBAEA,IAEA,2BACApzC,aACAvY,aACA2rD,oBAEA3rD,qCACAqW,wCACArW,aACA2rD,gBAEA,GACA3rD,uCACAqW,0CACArW,aACAhM,gCACA23D,gBAEA,IAEA3rD,oCACA,EAEAA,MACA4rD,aACAC,mBACAC,kBAGA9rD,0BACA+rD,OACA,GAAK,GAEL/rD,4BACA+rD,OACA,GAEA/rD,sBACAgsD,mBACAxiD,4BAA4CyiD,gBAAmBr+C,0BAC/DklB,IACA,EAAa,YACbA,GACA,EACA,EACAo5B,iBACAv0C,WACAC,4BACAC,uBACA8U,mEACAvC,QACAnlC,mBACA,SACA,+DADA,EAEA,EACA2d,qBACA,eACA,SACA,OACAupD,OADAlnE,cACA,kBAEA,2BAEAA,mCACAknE,iBACAlnE,SACAknE,gBACA,gFACAA,8BACA,IAIAnsD,sBACA2X,WACAC,wBACAC,wBACA8U,yCAGA3sB,oBACA4X,gBACAC,kBACA8U,sBAGA3sB,SACAA,mBAEAA,0BAA+Cu/C,4BAC/Cv/C,wBAA6Cu/C,iBAAkB6M,oBAC/DpsD,yBAA8Cu/C,6BAG9Cv/C,gBACAnb,4CACAmb,2CACAA,mBAAoCgP,6BAEpC,GAEA,IAgCAq9C,aAEA,0BACAj4D,QAA+BxO,gBAlC/B4X,KACA,uBACAA,0BAGAA,YACA3Y,mDACA,mCACAu2D,gBACA59C,sBACAA,wBACAA,6BAGAA,aAEA,IAGA3Y,iDACA2Y,MACA,GAEAA,WACA3Y,2CACA2Y,eACA,GAEAgM,+BAAiDhM,cAWjDhJ,iBACA,mDACAgV,2BAAkD5jB,SAAc,IAAGgoB,QACnE,GA3CA,IACApQ,CA2CA,EAEAwC,gCACAqsD,qBACA/3D,6BACA0L,gBACA,EACA,EAEAA,4BACAqsD,qBACA,IACAvmE,8BACA,eACAA,kBAEAka,wCACA,EACA,EAEAA,0BACA/G,aACA+G,gBACA,EAAS,IACT,CACA,IAKAD,mKACAC,mBACAnb,8BACA,0BAEAuc,SADAA,iBACA,wBAEA,wBAGA,+BACAA,oDACA,qCACA,qCACA,yDAEAA,kCACA,8BACA,6BACA,oCAEAA,4EACA,GACApB,UACAA,yBAEAA,WACA,EAEAA,UAEAA,oBACApC,QACA4L,6CACA5L,YACAoC,SACA,EAAS,YACTpC,YACA1J,gCACA,EACA,EAEA8L,qBACAmB,2BAAyCgX,mBACzC,EAEAnY,sBACAmB,mCAAiDgX,mBACjD,EAEAnY,wBACAssD,WACA/oD,sBACA7E,+FACSlK,iBACToJ,QACA,SACA/Y,8BAAkD0nE,eAClD/iD,uBAAuC+iD,QAAS3+C,yBAChDhQ,YACAoC,UACA,EAAa,YACbpC,YACA1J,mCACA,EACA,EACA,EAEA8L,oBACAssD,WACA/oD,sBACA7E,uJACSlK,iBACToJ,QACA,SACA/Y,8BAAkD0nE,eAClD/iD,mBAAmC+iD,QAAS3+C,yBAC5ChQ,YACAoC,UACA,EAAa,YACbpC,YACA1J,+BACA,EACA,EACA,EAEA8L,oBACAJ,QACAE,6DACAqT,qBACAC,+BACArT,oCACAoN,SACAmK,kBAAqC,OACrCk1C,wBACAC,aACAC,qBACAC,oCAEAC,gBACAznD,mBACA,GAEAhF,UACAmT,mBACS1U,uBACToB,UACA,EACA,EAGAA,yBACAJ,QACAE,kFACAqT,qBACAC,+BACAE,iBACAvT,uDACAC,WACAA,yBACAA,kBACAnb,8BACAmb,eAA4C6sD,sCAC5C,EACA,EACA7sD,kBACApC,QACA4L,iCAAyD5jB,SAAc,CAAGknE,iBAAqBl/C,yBAC/FhQ,YACAoC,UACA,EAAqB,YACrBpC,YACA1J,gCACA,EACA,CACA,GACAiZ,SACAmK,kBAAqC,aACrCy1C,kBACA,wCAAgEnnE,aAAegoB,QAC/E,KAEShP,wBACToB,UACA,EACA,EAGAA,uBACA,SACAnb,8BAA4C0nE,eAC5C3sD,QACAE,uEACAqT,qBACAC,+BACAE,iBACAvT,iCACAC,iBAAwCwX,oCAExCxX,oBADAgtD,WACA,CAA8Cx1C,kEAE9C,CAA8CA,0CAE9CxX,YACA4D,cACAqB,iBACAgoD,wBAEAjtD,uBACA,IAEA,EAEAA,+BACA,SACAnb,8BAA4C0nE,eAC5C3sD,QACAE,iFACAqT,qBACAC,+BACAE,iBACAvT,iCACAC,iBAAwCwX,oCAExCxX,oBADAgtD,WACA,CAA8Cx1C,kEAE9C,CAA8CA,0CAE9CxX,YACA4D,cACAqB,iBACAgoD,wBAEAjtD,uBACA,IAEA,EAEAA,oBACA+X,UACAxU,uBACAiT,kMAGAwB,gBACAvB,UACA7S,UACAspD,wBAEAx2C,QACA,CAAiBnU,2CACjByV,gKAEAlH,QACA,CAAyBrd,oDACzB,CAAyBA,sCACzB,CAAyBA,gDAGzB,CAAiB8O,kDACjByV,qTAKAlH,QACA,CAAyBrd,iCACzB,CAAyBA,iDACzB,CAAyBA,mCACzB,CAAyBA,sCAIzB3N,8BACS0O,gBACTwL,UACA,EACA,EAEAA,4BACA+X,UACAxU,+BACAiT,2XAKAwB,gBACAvB,UACA7S,WAEA8S,QACA,CAAiBnU,2CACjByV,gKAEAlH,QACA,CAAyBrd,oDACzB,CAAyBA,sCACzB,CAAyBA,iDAIzB3N,wCACS0O,gBACTwL,UACA,EAEA,CACA,IAEAD,uHACAC,kBACAA,WAEA,iCAEAA,gBADAA,wBACA,wBAEA,wBAGAsX,sDAEAtX,kCACAA,oCACAA,4DACAA,8DAEAnb,sFACAwlB,uCACA,GAEArK,6BACAA,gEAA6EmtD,4BAC7E,EACAntD,6BACAA,qEACA,EAEAA,oBACAA,WACAmB,6BAGAnN,+BACAgM,WAEA,EAEAA,mBACAhC,kBACAhK,oBACA,kCACAgK,uBACAA,qBAEA,oCACAA,mBACAA,mCAEAA,oCACAnZ,+EACAwlB,qBACA,GAEAzM,SAEAoC,yBAAyDpa,SAAYoY,qBACrE4P,yBACAhQ,YACAoC,WACAmB,4BAGAnB,UAEA,EAAS,YACTpC,YACA1J,8BACA,EACA,EAEA8L,+BACAA,mEACAnb,8BACAmb,gEAAiFmtD,wBACjF,EACA,CACA,IAEAptD,yOACAC,WAEAA,mBACAuI,sBACArjB,gCACA,GAEA8a,UACAA,yBAEAA,WACA,EAEAA,UAEAA,oBACApC,QACA4L,qBAAiC5jB,UAAcgoB,0BAC/ChQ,YACAoC,SACA,EAAS,YACTpC,YACA1J,gCACA,EACA,EAEA8L,kBACAmB,2BACA,EAEAnB,qBACAmB,kCAAgDgX,kCAChD,EAEAnY,wBACAssD,WACA/oD,sBACA7E,+FACSlK,iBACToJ,QACA,SACA/Y,8BAAkD0nE,eAClD/iD,uBAAuC+iD,QAAS3+C,yBAChDhQ,YACAoC,UACA,EAAa,YACbpC,YACA1J,mCACA,EACA,EACA,EAEA8L,oBACAssD,WACA/oD,sBACA7E,uJACSlK,iBACToJ,QACA,SACA/Y,8BAAkD0nE,eAClD/iD,mBAAmC+iD,QAAS3+C,yBAC5ChQ,YACAoC,UACA,EAAa,YACbpC,YACA1J,+BACA,EACA,EACA,EAEA8L,oBACAJ,QACAE,oEACAqT,qBACAC,+BACAE,iBACAvT,0CACAoN,SACAmK,kBAAqC,UACrC0pC,kBAAqC,OACrCoM,oCACAC,eACAb,0BACAc,iBACAC,0BACAC,2BACAjzB,oBACAkzB,sBACA,EACA1M,wDACA,qCAA6Dn7D,iBAA2BgoB,QACxF,IAEAzN,YACSvB,uBACToB,UACA,EACA,EAEAA,sBACAJ,QACAE,qEACAqT,qBACAC,+BACAE,iBACAvT,2CACAoN,SACA4zC,6BAAgD,UAChDzpC,kBAAqC,UACrC0pC,kBACA,WACA,+CACA0M,CACA,EACAC,kBAAqC,WAErCxtD,YACSvB,wBACToB,UACA,EACA,EAEAA,uBACA,SACAnb,8BAA4C0nE,eAC5C3sD,QACAE,uEACAqT,qBACAC,+BACAE,iBACAvT,iCACAC,iBAAwCwX,oDAExCxX,oBADAgtD,WACA,CAA8Cx1C,kEAE9C,CAA8CA,0CAE9CxX,YACA4D,cACAqB,iBACA2oD,wBAEA5tD,iBACAA,uBACA,IAEA,EAEAA,8BACA,SACAnb,8BAA4C0nE,eAC5C3sD,QACAE,+EACAqT,qBACAC,+BACAE,iBACAvT,iCACAC,iBAAwCwX,oDAExCxX,oBADAgtD,WACA,CAA8Cx1C,kEAE9C,CAA8CA,0CAE9CxX,YACA4D,cACAqB,iBACAvH,gBACAkwD,wBAEA5tD,iBACAA,uBACA,IAEA,EAEAA,oBACA+X,UACAxU,uBACAiT,kMAGAwB,gBACAvB,UACA7S,UACAspD,wBAEAx2C,QACA,CAAiBnU,2CACjByV,gKAEAlH,QACA,CAAyBrd,oDACzB,CAAyBA,sCACzB,CAAyBA,gDAGzB,CAAiB8O,kDACjByV,qUAKAlH,QACA,CAAyBrd,iCACzB,CAAyBA,iDACzB,CAAyBA,mCACzB,CAAyBA,sCAIzB3N,+CACS0O,gBACTwL,UACA,EACA,EAEAA,2BACA+X,UACAxU,4BACAiT,gSAIAwB,gBACAvB,UACA7S,UACAlG,gBACAmwD,kBAEAn3C,QACA,CAAiBnU,2CACjByV,gKAEAlH,QACA,CAAyBrd,oDACzB,CAAyBA,sCACzB,CAAyBA,gDAGzB,CAAiB8O,6CACjByV,yJAEAlH,QACA,CAAyBrd,mCACzB,CAAyBA,uCAGzB,CAAiB8O,+CACjByV,uMAGAlH,QACA,CAAyBrd,sCACzB,CAAyBA,uCAIzB3N,uDACS0O,gBACTwL,UACA,EACA,EAEAA,sBACAJ,QACAE,6EACAqT,qBACAC,+BACAE,iBACAnG,SACA2gD,mBACA,gBACA,0DACA,SACAjpE,8BAEA+O,mBADA,+BAGA,4BAFA,cAMA,OAGAA,uBADA,6BACA,eAEAA,sBAEAA,6BADA,mCACA,eAEAA,2BAEA,GACA/O,8BACA+O,8BACAk6D,QACAr6D,QACA+jB,6GAGA,GACAs2C,aACAA,iBAGAjpE,8BACA+O,8BACAk6D,QACAr6D,WACA+jB,sGAGA,GACA1Z,YACA,EAAqB,WAAeA,aACpCA,SACA,GAEAiC,6CACAC,YACAA,cACAnb,8BACA6O,aACAsM,0BACA,EACA,KACSpB,wBACTZ,GACA+vD,2CAEA,EACA,CAGA,IAEAhuD,4KAEAC,kBACAA,WACAA,WAGAA,iDACAA,gBAEAA,YACAguD,MACA,CAAapoE,qCACb,CAAaA,mDAEbqoE,KACA,CAAaroE,sCAEbsoE,MACA,CAAatoE,mCACb,CAAaA,oCAIboa,iBACAA,gBACA4X,mBACAC,mBACA8U,sBACA1lC,WAGA,iCAEA+Y,gBADAA,wBACA,wBAEA,wBAEAsX,sDAEAtX,kCACAA,oCACAA,4DACAA,8DAEAnb,sFACAwlB,uCACA,GAEArK,sDAGAA,oBACAA,WACAmB,mCAAqDgX,iBAGrDnY,UAEA,EAEAA,oBACAhC,mBAGAovD,qCAEAptD,sCACAA,iBAEApC,SACAoC,yBAAyDpa,6BAAuCoY,kBAA6BmwD,qBAA0BnwD,IAEvJ4P,yBACAhQ,YACAoC,WACAmB,mCAAyDgX,iBAGzDnY,UAEA,EAAS,YACTpC,YACA1J,8BACA,EACA,EAEA8L,qBACAJ,QACAE,qEACAqT,qBACAC,+BACAE,iBACAvT,2CACAoN,SACA4zC,6BAAgD,UAChDzpC,kBAAqC,UACrC0pC,kBAAqC,iBACrC2M,kBAAqC,WAErCxtD,YACSvB,wBACTZ,IACAgC,kCAEA,EACA,CACA,IAEAD,oJACA,SACAlb,8BACAupE,YACA,GAEApuD,gEAEAA,sBACAA,aACAnb,2CAEA,QADAwpE,8BACA15D,IAAsBA,WAAgBA,MACtC,WACA25D,SACA76D,8BAGA,GAFA,oBACAA,sBACA,uBACA,GACA,0BACA,IACA86D,GADAvuD,kBACAvM,kBACA+6D,qCACA,KACA,YAAkC5yC,WAAgBA,KAClDnoB,gBACA,YAA4C6G,MAAmBA,IAC/D,4CACA7G,iCACA,MAGAA,IAEA,OAhBAA,KAkBAuM,6BAAwDuC,gBAA8B+rD,GACtFtuD,qCACA,CACA,GAEAA,uBACAyQ,UACA,EAEAzQ,oBACAA,UACA,EAEAA,kBACA,uCACA4mC,KAyBA,GAxBA/hD,8BAGA,GAFA,oBACAypE,uBACA,6BACA,GACA,0BACA,IACAC,GADAvuD,kBACAvM,kBACA+6D,qCACA,WACA,YAAkC5yC,iBAAqBA,KACvD0yC,4BACA,YAA4Ch0D,MAAmBA,IAC/D,4CACAg0D,kCAEA,MAGAA,UAEA,OAjBAA,WAkBA1nB,iBACA,GACA+mB,GACA,sBACAc,2BACA7wD,QACA4L,cAA8B5jB,SAAkB6oE,4BAChD7wD,YACAoC,UACA,EAAa,YACbpC,YACA1J,8BACA,EACA,MAEA8L,WAEA,CACA,IAEAD,oHACAC,iBACA,CAASvM,8CAAmD,oCAC5D,CAASA,4CAAiD,kBAC1D,CAASA,wCAA6C,oBACtD,CAASA,0CAA+C,mBACxD,CAASA,gEAAqE,oBAC9E,CAASA,mDAAwD,oBACjE,CAASA,oEAAyE,qBAGlFuM,eACA,CAASvM,wHACT,CAASA,oHACT,CAASA,oHACT,CAASA,kHAGT,SACA5O,0CACA6pE,kBACA,GACAppE,qCAEAopE,KACA7pE,4CACA6pE,wBACA,GACAppE,wCAKA0a,4BACA+tD,4DACA,EAEA/tD,4BACA,MACA,mDACAqW,yBACAs4C,IACA,GAEA/uD,QACAE,qFACAqT,qBACAC,+BACAE,iBACAzT,kBACAE,4CACAoN,SACAyhD,qCACA,YACAplD,uBAA0D5jB,UAAiBgoB,SAG3EyX,CAEA,GACAwpC,wBACA,QACA,EACAC,yBACA,sBACA,GAEA3uD,YACSvB,MACT,CACA,IAEAmB,iKACAC,kBACAA,qBACAmB,2CAAyDvb,YACzD,EAEAoa,oBACAmB,2CAAyDvb,UACzD,EAEAoa,wBACAmB,8CACA,EAEAnB,oBACAwJ,mDACAxL,sBACAjI,8DACA,GACAiK,8BACA,EACA,EAEAA,sBACAA,qBACA,EAEAA,sBACAmB,gDAA8Dvb,YAC9D,EAEAoa,mBACAwJ,yBAAqC5jB,YAAe,IAAIgoB,0BACxDzM,2CAA6Dvb,SAC7D,EACA,EAEAoa,gCACAJ,QACAE,6FACAqT,qBACAC,+BACAE,iBACAzT,kBACAE,mDACAC,mBACAnb,8BACA,kBACAmb,uBACA,GAEAA,YAAmCmH,sBACnC,GACAgG,SACA40C,mCACA,wCACA,MAESnjD,wBACT6X,GACAjN,yBAA6C5jB,kBAAyB6wB,kCACtEtV,2CAAqEvb,SACrE,EAEA,EACA,EAEAoa,qBACAssD,WACA/oD,sBACA7E,4FACSlK,iBACToJ,QACA,SACA/Y,8BAAkD0nE,eAClD/iD,0BAA0C+iD,QAAS3+C,yBACnDhQ,YACAoC,UACA,EAAa,YACbpC,YACA1J,gCACA,EACA,EACA,CACA,IAGA6L,uKACAC,qBACAA,uCACAixB,8DACA,GACAjxB,qBACAA,sCACAA,UACA,EACA,EAEAA,qBACAmB,2CACA,EAEAnB,oBACAA,iBAA8BwxB,yDAA+Dh9B,iBAC7FwL,UACA,EACA,EAEAA,oBACAwJ,2BAAuChjB,eAAconB,0BACrD5N,kCACAA,uCACAixB,8DACA,EACA,EACA,EAEAjxB,qBACAssD,WACA/oD,sBACA7E,kGACSlK,iBACToJ,QACA,SACA/Y,8BAAkD0nE,eAClD/iD,6BAA6C+iD,QAAS3+C,yBACtDhQ,YACAoC,UACA,EAAa,YACbpC,YACA1J,gCACA,EACA,EACA,EAEA8L,sBACAJ,QACAE,wFACSlB,wBACT,MACA,SACA/Z,8BAAsD0nE,eACtD3uD,OACA4L,8BAAsD+iD,mBAAuB3+C,SAC7E,gCACApZ,gBACAwL,UACA,EACA,CACA,EACA,CACA,IAEAD,0MAEAC,YACAA,mBAA4Bs+B,sDAC5Bt+B,sBAEAA,aAEAA,WAEAiiD,cACAnvC,UACAi8C,SACAne,OACAltC,QACAsrD,aACAjnE,OACAknE,gBAEA,SACApqE,gDACAosC,WACA/Z,aACA+Z,uCAGAA,oCAEApsC,yCACAwxB,wCACA,GACA4rC,mCACA,UACAA,SACAjiD,sBAEAA,iBACAiiD,uBACAz4C,qBAAiC5jB,2BAAqCgoB,0BACtE5N,WACA,GAGAA,yBAEAA,6BACAA,+BACAnb,4CAEAmb,sBADA2uD,SACA,CACA,CAAiBO,iFACjB,CAAiBA,2GAEjB,GACArqE,8BACAkR,sDAEAA,eADAA,UACAA,uBAGAA,qBAEAiK,6CAEA,EACA,EAEAA,8BACA,gBACA,6DACAA,oBACAlC,YACA,EAAS,WACTA,UACA,GACAA,SACA,EAEAkC,oBAEAA,mBACA,6BACAhC,aAEA,SACAnZ,4CAEAA,6BADAsqE,SACA,YACA,SACA,OAAqCvpE,mCACrCf,yCACAuqE,wCACA,GACAjN,SACA,CACA,EACA,GACAnkD,eACAJ,SACAI,yBAAuDpY,SAAYoY,0BAAoCA,IACvG4P,0BACAhQ,YACAuD,2CACA,EAAS,YACTvD,YACA1J,8BACA,EACA,EAEA8L,oBACAmB,2CACA,EAEAnB,8CACA,SACAA,8BACAnb,wDACAoR,sBACA,iCACAihB,aACAlX,8BAEA,EACA,GACAA,qBACA,GAAK,GAGLA,qCACA,gBACA,2CACAhC,GACAA,sCACAnZ,oBACAmb,qCACAlC,YACA,EAAiB,WACjBA,YACA,IAGAA,UAEA,GACAA,SACA,EAEAkC,gBACA8S,YACAi8C,WACAne,SACAltC,UACAsrD,eACAjnE,SACAknE,kBAGAjvD,sCACAnb,gCACA,6BACA,WACAwgC,SACAzlB,QACAE,mFACAqT,qBACAC,+BACArT,yDACAC,wBACAA,YAA+CmH,cAC/C,GACAgG,SACAkiD,+BACA,SACA,4DACAjuD,SACAvC,SAEA,GACAA,CACA,GAEAsB,YACqBvB,wBACrBwT,KACAiT,mBACA9iB,uBACA8iB,UACAA,WACArlB,6CACAA,oCACA,IAEAA,sBACA,GAEAqlB,SACArlB,0BAAmDwxB,wDAAgEh9B,iBACnHwL,oCACA,GACAA,yBAGAA,qCACAA,uBAEA,CACA,EACA,GAAK,GAELA,0DACAuY,kBACA0pC,yBACAz4C,qBAAqC5jB,OAAWgoB,0BAChD5N,WACA,GAEA,EAOA,IACAD,oLACAC,oBACAA,kCACAA,iBACAA,kBACAA,4DACA4uD,gCAEA5uD,wBACA+X,UACAxU,sBACAiT,gCACAwB,gBACAvB,YACAC,UACA5wB,sDACS0O,iBACT3P,wCACA,EACA,EAEAmb,mBACApC,SACAoC,kCAAkEpa,SAAYoY,6BAAuCA,IACrH4P,0BACAhQ,YACAoC,WACA,EAAS,YACTpC,YACA1J,8BACA,EACA,CAEA,IAEA6L,wLACAC,SAEAA,kBACAmB,2CACA,EAEAnB,oBACAwJ,wBAAoC5jB,UAAoBgoB,0BACxD5N,qBACA,EACA,EAEAA,mBACAJ,QACAE,2FACAqT,qBACAC,+BACAE,iBACAzT,kBACAM,UACAJ,wEACAC,SACAA,cACAnb,yCACAsrB,sBACA,GAEAnS,WACAgC,sIAEAhC,gBACAgC,0IAEA,GACAmN,SACAnP,gBAAmC,qBACnCqa,4BAA+C,gCAG/C,EAEArY,qBACAssD,WACA/oD,sBACA7E,8FACSlK,iBACToJ,QACA,SACA/Y,8BAAkD0nE,eAClD/iD,0BAA0CoF,wBAA2BhB,0BACrEhQ,YACAoC,UACA,EAAa,YACbpC,YACA1J,gCACA,EACA,EACA,CACA,IAEA6L,sJACAC,cACAA,SACAtE,cACAvK,YAGA6O,sBACA,OAAoB,mEAEpB1L,+FACA0L,sBACAA,+BACAvZ,+BACAuZ,mBAEA,EACA,EAAK,YACLhM,yEACA,GAEAgM,gBAEAA,0BACAJ,QACAE,sEACAI,oBACAH,iCACAC,UACA,IAEA,EAEAA,eACAsvD,kBACA1I,iBACAC,wBACAC,mBACAC,iBACAC,kBACAC,0BACAsI,yBAAkCC,YAClCC,mBAA6BlmD,yCAC7BmmD,gCACA,OAAqBnmD,8DACrB,EACAomD,uCACA,6BAAiDH,wCACjDlI,sBAAiDkI,wCACjDlI,CACA,EACAJ,qMACAC,YACA,CAAczrD,kCACd,CAAcA,4CACd,CAAcA,eACd,CAAcA,gBACd,CAAcA,iBACd,CAAcA,wCACd,CAAcA,kDACd,CAAcA,kDACd,CAAcA,4CACd,CAAcA,+CACd,CAAcA,gBACd,CAAcA,eACd,CAAcA,6DACd,CAAcA,4CAIdsE,sBACAA,aAAgCA,0CAEhCpC,QAEAvJ,2BADA2L,iDACA,CAA0DvC,kBAAoBjJ,iBAC9E3P,mCAEA,SAEA+P,kBACA2pB,oBAGA3pB,kBACA2pB,2BAEA3pB,kBACA2pB,0BAGA3pB,UACA2pB,mBAGA3pB,SACA2pB,gBAGA3pB,SACA2pB,oBAGA3pB,YACAA,0DAGAA,kBADA,2BACA,4BAGAA,UAIAA,kBADA,8BACA,KAGA,KAEA,GACAoL,oCACAA,oBACApC,WACA,EACA,EAEAoC,wCACA4vD,gCAEA,cACA,wCACA,eACAC,sBACA,iBACAA,KAIA,IACAC,cADAx7D,gCACuCu7D,GACvC,UACApjB,wBACAqjB,YACA9vD,+CACA4a,mBACA5a,oCAEA,EAEA,GAEAysC,CACA,GAEAzsC,cACAA,2DACA4wC,aACA5wC,gBAGAA,eAEA,GACAA,gEACAkS,sBACA0+B,aACA5wC,gBAGAA,eAEA,EACA,EACA,EAEAA,uBACA,8CACA2Y,sDACA,EAEA3Y,wBACA,8CACAvG,iEACA,EAEAuG,yBACA,8CAEAw1B,uDACA4xB,sBACA/yD,mDAAiFvN,8CACjF,CAA8ByN,2BAC9B,GAGAyL,0CACAA,YACA,EAEAA,wBACA,8CACAA,0CAEAonD,sBAEA/yD,kDADA,CAA2BvN,sEAC3B,CAAgFyN,4BAChFC,gBACAN,OAAsCC,kDACtC6L,YACA,EAAiB,YACjBhM,iBACAE,OAAsCC,4DACtC,EACA,EACA,EAEA6L,uBACA,8CACA+vD,KAqBA,GAnBA/vD,0CAEAonD,sBACA9hD,qBACAyqD,kBAIA17D,kDADA,CAA+BvN,qEAC/B,CAAoFyN,4BACpFC,gBACAN,OAA0CC,kDAC1C6L,YACA,EAAqB,YACrBhM,iBACAE,OAA0CC,4DAC1C,EAEA,GAEA47D,YACA,wFACApqD,QACA,CACA,EAEA3F,6BACA,IACA4wC,EADA5wC,wCACA,GAEAA,0CAEA,gCACAJ,QACAC,YACAC,8EACAC,uDACAC,iBACAA,4BACAC,UACA,CACA,KAGArB,wBACA,0DACA,GAGAoB,uDAGA,EAEAA,8BACA2Y,qCAGAtkB,qCADA,CAAuBvN,+BACvB,CAAyEyN,4BACzEC,gBACAN,OAAkCC,uCAClC6L,YACA,EAAa,YACbhM,iBACAE,+CACA,EACA,EAEA8L,uBACAA,qCACA,EAEAA,sBACAA,yCACA,EAEAA,YACA,IAEA0gC,0GASA,OACA5gC,kEACAmF,OAAiB+qD,gBACjBzpB,cAEA37B,uBAOA3F,0BACAA,oCAEAA,4DAEAA,WAEA/e,SACA+pE,eACAC,aACA9lB,WACAxsC,YAGAqH,yBAEAA,yCACAhM,aACAgM,wCAGA,EACA,EAEAA,wBACAA,iBACAA,sBAEA,EAEAA,iBACAA,gBACAu7B,cACAr/B,oBAA4DyL,qBAA2B,CAAI0b,uBACzErjB,YAClBA,YAAqCkrD,UAErC,EAEAlrD,iCAEA9D,6BAAoDw9C,OADpD15C,sDACmE,CAAIqjB,YACvErjB,eACA,EAEA,4CAEAA,8BACAA,wBACA,OACA9e,SACAK,cACA4pE,iBACA9xB,0BACA+xB,2BAGA/tD,YACA,gBACA/b,gBACA,WACAA,mBACAA,oBAEAA,mBACAA,oBAGAA,gBAGA8N,qBAAkD9N,WAAkBiO,iBACpE,aASA,WARA87D,oBACA,OACA1qE,QACAopB,+DACA0pB,yBAEA,IAEAngC,4BACA0M,WACA,IAKAqrD,CACA,EACA,CACA,EAEA,IAEAvwD,mLACAC,aAEA,OACAuwD,mBACAC,yBACAC,yBACAC,0BACAC,0BACAC,eACAC,qBACAC,iBACAC,mCAqEA/wD,gDACAA,UACAnH,cACAm4D,cACAC,kBACAC,gBACAh2B,YACAi2B,SACAC,SAEA98D,6CACA0L,wBAGAA,yBACAA,eACA,EAEAA,sBACAA,aACA,EAEAA,yBAEAA,gBADA,YACA,EAEAnH,CACA,EAEAmH,wBAEAA,eADA,YACA,EAEAgxD,EACAhxD,2BACAA,wBACA,EAEAA,YACA2jB,QACAstC,cACAC,aAEAltC,wBACAC,sBACA,0BACAjkB,mCACA,wBACAA,4BAEAA,sBAEA/G,aACA+G,uBACA,EACA,GAGAA,gCAKA,GAJAuY,MACAjkB,6CAGAikB,GACAvY,iCACA,SACAzZ,cACAgyB,WACAhyB,kBACAyZ,oCAIAA,uBADA,gBACA,oCAGA,YAGAuY,UACAhyB,gBACAyZ,iDAEAuY,eACAhyB,6BACAyZ,6DAEAuY,aACAhyB,yBACAyZ,yDAEAuY,wBACAhyB,UACA,cACAyZ,6CAEA,gBACAA,WAGAA,SAGA,CACA,GAAK,GAIL,kBACA,kBACAqxD,cACAA,OACAA,SACA,qBAKA,OAJAC,OACAA,SACA19D,gBACA,WAEA,EAEAoM,4BACA,iEACAuxD,oBACA,KACA,UAA0B9rE,8DAC1B,EAEAua,UACAA,mBACApC,QACA4L,iDACA3kB,+BAtMA,YAIA,GAHA2sE,cACAtsE,uBAEAA,kBACA,eAEA,sBACA,eAEAA,mDADAA,wCACA,OACA,MACA,kBACAA,sEACA,MACA,UACAA,iDACA,MACA,WACAA,WAEAA,2FAEA,MACA,mBACAA,gFACA,MACA,YACAA,WAEAA,4FAEA,MACA,mBACAA,yEACA,MACA,aACAA,cACAA,iGACA,MACA,QACAA,SAGA,CAEAA,QACAirB,yBAEAjrB,cACAA,6EAEA,aACAA,4BAEA,cACAA,+BAEAA,SACAA,+EAEAA,eACAirB,4BAEA,CAuIAshD,GACA,GACA7zD,YAEAoC,qBACA,EAAS,YACT9L,8BACA0J,WACA,EACA,EAEAoC,aACAhC,QACAmpD,cAEAP,iBACA8K,oBACA7K,wBACA8K,oBACAC,qBACAC,sBACAC,4BACA9K,kBACA+K,6BACAC,eACA1C,mBAGAtvD,oBACApC,QACA4L,kDACAxV,eACAgM,0BACAnb,mCAEA,QADAuzB,KACAzjB,IAA8BA,mBAAqBA,IACnDyjB,aAEApY,wBACA,GAEAA,gCACA,YAA0BrL,mBAAqBA,IAC/C,2BAGAqL,6BACAtE,WACAu2D,YACAC,2BAIAt0D,WACA,EAAS,YACT1J,8BACA0J,WACA,EACA,EAGAoC,sBACAJ,QACA2D,sBACAzD,sEACAC,iCACAC,eACAA,UACAnb,8BACAstE,iBACAnyD,aAA0CtE,+CAC1C,EACA,IAEA,EAEAsE,sBACAJ,QACA2D,eACAzD,qEACAqN,SACAkX,kBACA,eACA,GAEAtkB,4CACAC,gBACAA,WACA,SACAqkB,WACA99B,mBACA89B,UACA99B,iBACA89B,eACA99B,8BACA89B,aACA99B,0BACA89B,OACA99B,WACAA,cAEA,SACA1B,gCACAwxB,mCACA,GAEArW,uBACA,IAEA,EAEAA,oBACAJ,QACA2D,cACAzD,oEACAqN,SACAkX,kBACA,eACA,GAEAtkB,4CACAC,UACAA,WACAA,qBACApC,QACA4L,mBAA2C4oD,OAAU,IAAGxkD,0BACxD1Z,OAA0CC,0CAC1CyJ,YACAoC,iBACAA,UACA,EAAqB,YACrB9L,gCACA8L,UACA,EACA,CACA,IAEA,CACA,IACAD,yFAEAC,sCAAqDqyD,UAAY,YACjEr+D,eACAs+D,sBAEA,EACA,GAEAtyD,yBACA,yBAA8C2K,sBAA0B4nD,6BACxEv+D,eACAgM,sBACA,EAEAA,2BACA,0BACA,mDACAwyD,0CACA,CACA,QACAA,UAA4B,YAC5Bx+D,cACA,EAAa,YACbE,8BACA,OAEU,CACV,YAA4BS,wBAAgCA,IAC5D,kGAEA,YADAT,OAAsCC,6CAItCq+D,SAA8B1rE,aAAc,YAC5CkN,eACAE,OAAkCC,8CAClC,EACA,CAEA,EACA6L,yBACAwyD,aAA2B,YAC3Bx+D,eACAA,eACA,kCAAyDW,KAAQA,IACjE69D,uDAEAt+D,OAA8BC,4CAE9B,EAAS,YACTH,gBACAE,OAA8BC,oFAE9B,EACA,CACA,IAEA4L,0LACAza,WAEA,8BAEA0a,QACAyyD,sBACAC,aACAC,eAGA3yD,QACAqrB,MAAgBunC,sBAChBpsE,QACAqwC,OACA+7B,OACA3+B,YACA4+B,qBACA,sCAA0D,oBAC1D,EACAC,qBACA,iCACAjuE,uCACAgyC,iDACA,EACA,GAEA,iBAGA,SAkBA,GAjBA72B,kBACAnb,wCACA,4CACAk3C,SACA,GACAA,6BAYA/7B,iBACA,MAGA+yD,EADA,2BACAv6D,6CACA,uBAbA,gBACA,6BACA,yCACA,yBACA,GAAiBD,MACjB,GAAaA,MACb,CAOAy6D,OACAz8D,4BACA,GAEAiC,0BAGAujC,aACA,CACA/7B,aACA,EACAA,kCAEA,iBACApC,oBACAoC,oBACA,0BAAmD42B,aAAqB,YACxEE,sBAAwC92B,oBACxC,GAAS4N,SACTqlD,0BAA2Dr8B,aAAqB,YAChFs8B,sBACAlzD,mBACA4yD,mDAEA,EACA,GAAShlD,SACT5N,oCAAwD42B,8BACxDxiC,sDACAwJ,YACAu1D,IACAv1D,uBACA,EACA,EAEAw1D,aACA/+D,4DACA2L,8BACAA,gDACAA,mCACAA,8CACAA,4DACAA,uBACAA,yBAA4D82B,qBAC5D92B,4DACA,EACA,EAKA,aACA,6BACAwJ,qCAAwD5jB,OAAagoB,0BACrEwlD,IACAC,IACAz1D,yBACA,EAAS,YACTw1D,IACAC,IACAr/D,gBACA4J,yBACA,EACA,CACAzT,IAEA,aAA2C2sC,wBAC3C,CAAUw8B,+BAAoC,GAE9Ch/D,OAAwBwiC,oBAAiBy8B,uBAEzCj/D,OAAwBwiC,iBACxBy8B,oBACAA,wBAEAvzD,wFAAmG,cACnGA,iBAEAA,wCAEAA,cACAA,iBACAA,aAAyBk3B,8BACzB1tB,yBAAgC,YAChCmtB,sBACA,mCACA32B,4BAEAuzD,mBACAf,wBACAxyD,gCAGAA,wCACA,EACA,GAUAA,4BAGApC,wBACA,SACA/Y,uCACAgyC,eACA28B,yBACA,GACA3uE,uCACA+tE,eACAY,yBACA,GACAp/D,yBACAwJ,4BACAu1D,GACA,EACA,EAEAnzD,QAAoBtE,8BAEpBsE,0BACAA,8CACA62B,eACA72B,oBACA,EAEAA,8BACA,QACAwyD,kBACAxyD,+DAIAJ,QACA2D,mBACAzD,uEAEAD,YAEA4zD,mBACA1zD,wCAGAC,iDACA0zD,IACA1zD,yBACAA,eACA0zD,SACA1zD,wCACA,wBAA+D,IAG/DA,gCACA1a,uEAEA,GAEA0a,wBACAA,4CACAA,yBACA1a,2BACA0a,sDACAA,8CACAA,wBACAA,UACAwW,eACA3sB,uBACA8pE,cACA/mD,iBACA0xB,2BAQAt+B,uBACA,+CAEApC,aACA,6BAGA,GAFAg2D,2CAAkF,eAElFA,iCAEA,gBAEA,GADAtuE,yDACAuuE,KAGA,OAFAD,UAIA,CACA,qCACA5/D,eACA68C,mBACAA,mBACAuiB,IACAD,IAEAv1D,iBACAoC,UACA,EAAyB,YACzBhM,gBACA4J,gBACA,EAEA,KAAsB,CAEtBA,eAEA,IAGAk5B,EADAh4B,KAEAkB,sBACA82B,gBAQAA,sBACA,IAEA88B,oBAA0D5zD,UAAmB2zD,OAF7E3zD,+CACAA,yBAEA4zD,sBAEA,6BACA5/D,cACA,GACA8K,SACA,GACA1K,0BACAwJ,mBAjEA,YACAw1D,IAlFA,aAGAU,2BACAj9B,QACA,EACA,CA6EAk9B,iBACA,CA+DAC,GACA,EAAyB,YACzBhgE,gBACA4J,kBACA,EACA,CACAoC,UACA,CACA,IAEA,EAEAA,8BACA,QACAi0D,aACAr0D,QACA2D,2BACAzD,oEAEA2zD,mBAEA1zD,wCAEAC,OACAwW,eACA3sB,uBACA8pE,cACA/mD,iBACA0xB,2BAEAt+B,eACA/a,UACAyoD,cAGA1tC,uCACAwyD,8BACAxyD,sCAEA,GACAA,mDACA,0BACAy4B,2BACA,CAA8By7B,oEAE9Bz7B,CACA,EAAiB,uBAA0B,sEAE3Cz4B,qBACAm0D,2BAGAv2D,gBACAoC,qBACAA,gDAEAwJ,+BACAqnC,mBACAA,0BACA1mD,KAEAipE,IACAD,KAEAv1D,oBACAoC,UACA,EAAqB,YACrBhM,gBACA4J,oBACA1J,OAA0CC,wEAC1C,GACA,CACA,IAEA,EAEA6L,+BACA,SACAo0D,EACAC,UAEAxvE,uCACA+tE,uCACA,GAGA,QACA0B,cACA10D,QACAE,oEAMAC,wCACAC,YACAA,0CACAA,cACAq0D,sBACArgE,eACAA,4BACA4+D,gBAGAA,mCACAA,oBACAv+D,wCAA2ElO,WAAYqO,iBACvFpP,OACAyS,6BACAmI,oBACA,GAEA4yD,+BACA5yD,qBAGA,GACAA,uBACAxH,yCAAuE,uBACvE67D,sBAAuDr0D,sBAEvDq0D,sBAAuDr0D,qBAEvD,EACAA,cACAA,yBACA,8BACA,yCACA,EAAqB,EACrB,EAGAA,2BAEAglB,UAEAtmB,4EACA,qDACqBE,wBAErB,GADAoB,WACAu5B,EACA,wBACA,SACA86B,sBACA,mCAEA,iBACA,yBACAv1D,mBACA,GACA1K,yBACAwJ,2BACAzT,GACA,EAAyB,YACzByT,0BACA,EAAyB,CACzB,EACA,EACAoC,uBACApC,uBACA,SACAy2D,sBACA,mCAEA,mBAA8CxqE,uBAC9CiV,mBACA,GACA1K,yBACAwJ,2BACAzT,GACA,EAAqB,YACrByT,0BACA,GACAoC,UACA,CACA,IAEA,EAEAA,iCACAJ,QACAE,4EACAC,iCACAC,mBACAwJ,4BAAgD5jB,OAAagoB,0BAC7D5N,+BACA,EAAiB,YACjBhM,gBACAE,gDACA,GACA8L,0BACAwJ,8BAAsD5jB,OAAagoB,0BACnE1Z,OAA0CC,2CAC1C,EAAqB,YACrBH,gBACAE,sCACA,EACA,CACA,IAEA,EAEA8L,2BAEA,8BAEAu0D,GAEAC,mCACAC,8BAEAjC,iEAEAxtC,UACAvmB,mBACAC,6CACAC,UAAuB1I,0CACvB,CAAyBA,sBAChB2I,wBACTC,IACAjB,oBACAg1D,YAA+B/oE,eAAuB,WACtD+T,wBAEAy1D,IACAD,GACA,EAAiB,YAAex1D,0BAEhC,EACA,EAEAoC,yBACA,cAAgC82B,kBAAe87B,UAC/C,EACA5yD,sBACAy0D,+BACAD,oCACAE,mBAGA10D,mBAAwC82B,oBACxC92B,4BACA,mCACAA,yBACA7L,wGAEA6wB,UAAyBtmB,YAAcE,wBACvCC,GAGA2K,2BAA+C5jB,OAAagoB,cAC5D,YACAwlD,GACA,EAAiB,YACjBp/D,gBACAE,sCACA,EAEA,EACA,EACA8L,+EACA,IAEA0gC,6EACA,OACAz7B,OACA0vD,wBAKA70D,sEA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4Cu5D,kBAE5Cv5D,qBAEApC,OACA4L,qBAAqCjG,gCAFrC,qBAEqC+M,0DAA+I1C,SACpL,kBACApZ,iBACAwL,gBACAA,kCACAqW,0EACA,2BAAoDgjD,gBAAgB3iE,cACpE,GACA2f,wEACA,EACA,EACA,EAEArW,yBACAA,WACA,EAEAA,yBACAA,gBACAA,WACA,EAEAA,gCAGAglB,SADA,2BACA,CACAvmB,sCACAC,+HACAC,UAA2B1I,mDAAuD,CAAGA,iDAAqD,CAAGA,yBAI7I,CACAwI,wBACAC,2IACAC,UAA2B1I,2CAAiD,CAAGA,0BAG/E2I,yBACA,OACA4K,6BAAiD5jB,aAAiB,IAAGgoB,0BACrE1Z,OAAsCC,4BACtCgN,8BAAuD,CAAGmnB,WAC1D,EAAiB,YACjBp0B,gCACA,EAEA,EACA,CACA,IAEA6L,gPACAnC,mBACAoC,eAIA83C,MA0CA93C,WAA0B0gD,SAC1B1gD,YACA83C,sCA3CAA,kBACAA,UACAA,sBACAA,kBACAA,0BACAA,0BACAA,+BACAA,mBACA35C,GACAolB,QACAi2C,aACAC,OACAC,YAEAnoD,GACAgS,QACAi2C,aACAC,OACAC,YAGA5hB,kBACA35C,GACAmS,UACAqpD,iBAEApoD,GACAjB,UACAqpD,iBAGA7hB,wEACA93C,WAA0B0gD,SAC1B1gD,UACA45D,cACAnoE,YACAooE,YACAC,iBASA95D,kBACAA,mBAEAA,aAAwBtE,gCACxBsE,iBAA4BtE,qCAE5B7W,4CACA6O,sDACA,GAGAsM,iFACAA,iBAEAA,WAEAA,uBACAA,sCACAA,uBACAA,qBACAwJ,wBAAwCvG,4BAAgC2K,0BACxE/O,sBACAwX,uBACArW,2BAEA,EACA,EAEA,EACAA,4CACAuY,OACAvY,4BAEA,GAEAA,+BACA,qEACA,oEACA6wC,IACAjzC,QACAgC,QACA2D,4BACAzD,qEACAK,UACAszD,mBACAtmD,SACAnJ,mBACA,wBAAgD9d,+BAAsC0nB,0BACtF,kCACAmsD,sBACA,GACAl7D,CACA,EACA,EACA0E,iBACA,8BACA,GAEAxD,uDACAnC,YACAoC,gBACAA,yBACA,2BACApC,QACA4L,iBAAyCtjB,2BAAsC0nB,0BAC/E/O,2BACAk7D,sBACA,GACA/5D,YACApC,WACA,EACA,EAEAoC,YACAA,qBACA6wC,yBACAA,gCACA7wC,UACA,CACA,IACS,CACT,EAEAA,+BAGAglB,SADA,2BACA,CACAvmB,sCACAC,+HACAC,UAA2B1I,mDAAuD,CAAGA,iDAAqD,CAAGA,yBAI7I,CACAwI,wBACAC,2IACAC,UAA2B1I,2CAAiD,CAAGA,0BAG/E2I,yBACA,OACA4K,6BAAiD5jB,2BAAmC,IAAGgoB,0BACvF1Z,OAAsCC,4BACtCgN,2BACA,EAAiB,YACjBjN,gCACA,EAEA,EACA,EAGA8L,6BACAA,kCACAA,WACAA,kBACAA,8CACAA,0CAEAA,uBACAA,oBAEA,EAEAA,mCACAA,WACAA,oBACAnb,oCACAmb,kBACAA,8CACAA,yCAEA,EAEAA,iCACA,IAIAW,EAJAylD,KACAxoD,QACAoC,8BACAzJ,uFAEAyJ,gBACAW,oBAA4C/a,oBAAyBoa,wBAGrEW,2CACAylD,MAGAzlD,mBACA,SACAvM,UAGAoV,kBAA6C5jB,qBAAyBgoB,QAEtE,GAASpZ,iBACT4xD,GACAjlD,iCAAuDvb,UAEvDf,+BACAmb,iFACAA,kBACAA,8CACAA,0CACAA,0CACAA,WACApC,WACA,EAAS,YACTA,YACA1J,8BACA,EACA,EAEA8L,iCACAA,qCACAA,wBACAA,sBACA,EAGAA,gCACA,yCACAA,2BACA,6FACAwJ,sBAAsCwwD,6CAAwD,CAC9FC,uCACAC,6CACAC,mBACAC,2CACAC,2CACazsD,0BACb5N,gBACAnB,gBACAmB,4CACAA,6DACAA,yDACAA,wDACAA,oDACAnB,yBACAA,2BACAmB,kCACAA,oCACA,iDACAA,gEAA0GyQ,EAC1G,EACA,IAGAzQ,2BACAA,oBACA,EAAa,YACbA,2BAGAA,kBAFAwK,OACA,gCACA,0DAEA,gCACA,0DAGAA,OAIAA,CAEA,EACA,CACA,EAIAxK,8BACAA,WACAA,0CACAixD,mBACAC,iBACAnc,eACAulB,2BACAC,uBACAC,qBACAC,yBAEAz6D,qBACA,EAEAA,+BACA,OACAi6D,4CACA13D,iCACAiU,yCAEAhN,0DACAtV,OAA8BC,+EAC9B,EAAS,YACTD,gDACA,EACA,EAEA8L,8BACAA,WACAA,yCACA,EAEAA,+BACA,QACAJ,QACA2D,wCACAzD,mEACAqN,SACAutD,gCACA,uCACA,EACA3lB,mBACA,qDACA,oCACA9jB,0BAEAA,CACA,EACA0pC,oCACA,+CACA,EACAC,uCACA,kDACA,EACAC,qCACA,gDACA,GAEA16D,UACAJ,+KACAC,YAEAA,2BACA,KACA,0BACA,eACA,uBACA,eACA,eACA,EAEAA,uBACAJ,QACAE,iEACAK,WAEA,EAEAH,2BAEAA,6CACA,iBACAqW,8DAEAjV,oBACAA,mBAGAA,gCAEA,GACApB,oEACA,iDACA,GACAA,2BACAA,+BACAkT,6BAIAlT,8BACAA,oCAEiB,IAFjBA,6CACA,gDACA,IACAA,kCACAkT,yCAIAlT,4BACAA,kCAEiB,IAFjBA,2CACA,8CACA,IACAA,gCACAkT,uCAIA,uBAEAlT,uBACA2X,WACAC,sBACAC,kBACA8U,mCACA1lC,UACAmjC,QACAnlC,mBACA,eACA,cACA,OACA6lB,GADA7lB,OACAygE,UAEA,aACA,QAEA,EACA9iD,qBACA,eACA,cACA,OACAkI,GADA7lB,OACAygE,UAEA,YACA56C,YAEAA,GADA7lB,eACA,qBAEAygE,iBACA56C,UACA7lB,gBACA6lB,sDACAA,UAEA,IAKA9K,wCACA2X,WACAC,sBACAC,kBACA8U,mCACA1lC,UACAmjC,QACAnlC,mBACA,eACA,cACA,OACA6lB,GADA7lB,OACAygE,UAEA,aACA,4BAEA,EACA9iD,qBACA,eACA,cACA,OACAkI,GADA7lB,OACAygE,UAEA,aACA,4BAEA,IAIA1lD,uDACAA,qDAEAA,mDACAA,iDAEAA,uCACA,MAEA,GADAA,iCACA,oBACA,kBACAA,oEACA,cACA,EACA,MAEAA,oEACA,oBACA,GAEA,+BACA+0C,sDACA/0C,yCACA,6CACAA,uCACAA,sCACAA,mCACAA,kCACAA,uDACAA,sDACAA,mDACAA,gDACA,CAEA,CAEA,GAGAA,kBACAA,uCACAwJ,iCACAjH,2CACAiU,kDACAtD,yCACyBtF,0BACzB1Z,OAA8CC,uDAC9C,EAAyB,YACzBD,iDACA,GAEA8L,qCACAwJ,+BACAjH,yCACAiU,gDACAtD,uCACyBtF,0BACzB1Z,OAA8CC,wDAC9C,EAAyB,YACzBD,kDACA,GAGA,gCACAmiB,mBACAw6B,iCACAx6B,+BACAw6B,qCACA,KAGA7wC,mEACAA,6DACAnb,6BAGAgsD,WACA7wC,UACA,CACA,IAEA,EAGAA,qDACAuY,IAEAvY,uBACA60C,0BACA70C,YAGA,GAAK,GAIL,oBAGAA,oDACAA,oDACA,kBACA,kCACAuY,wBAEA,GACAA,IACAvY,wBAEA60C,0BACA70C,YAGA,GAAK,GAELA,mDACAA,oDACA,6CACA,kCACAuY,4BAEA,GACAA,IACAvY,wBAEA60C,0BACA70C,YAGA,GAAK,GAELA,0DACAuY,IACAvY,wBAEA60C,WACA70C,YAGA,GAEAA,0CACAuY,GACAvY,sBAEA,GAEAA,uBACA,gDACAA,wCAEAA,iCACAujB,QACAi2C,aACAC,OACAC,WAEA15D,gCACAsQ,UACAqpD,gBAEA,EAEA35D,uBACA,uFACAA,uCACAA,+BACAA,qCACA,EAEAA,8BACAmB,mCAAiDvb,8BACjD,EAEAoa,8BAGAglB,SADA,2BACA,CACAvmB,sCACAC,6HACAC,UAA2B1I,mDAAuD,CAAGA,iDAAqD,CAAGA,yBAI7I,CACAwI,wBACAC,0IACAC,UAA2B1I,2CAAiD,CAAGA,0BAG/E2I,wBACA5K,gBACA,OACAwV,+BAAmD5jB,aAAgB,IAAGgoB,0BACtE1Z,OAAsCC,6BACtCqV,+BAAuD5jB,qBAAyBgoB,0BAChF5N,kBACAnb,4CACA6O,sDACA,EACA,EACA,EAAiB,YACjBQ,gCACA,EAEA,EACA,EAEA8L,0BACAmB,8BAA4Cvb,MAC5C,EAEAoa,wBACAwJ,8BAA0C5jB,oBAAyB,IAAGgoB,0BACtE,uCAAuDhoB,qBAAyBgoB,QAChF,GAASpZ,iBACT,0BACAgV,kBAAyC5jB,qBAAyBgoB,QAClE,GAASpZ,iBACT3P,+BACAmb,gFACA,EAAS,YACT9L,gBACA,EACA,EAEA8L,+BACAJ,QACA2D,+BACAzD,6EACAC,wCACAoN,SACA2qC,sBACA,mBACA,KAESl5C,uBACT4K,gCAAgD5jB,qBAAyBgoB,0BAEzE,0BACApE,kBAA6C5jB,qBAAyBgoB,QACtE,GAAapZ,iBACb3P,+BACAmb,gFACA,EAAa,YACb9L,gBACA,EACA,EACA,EAEA8L,yBAGAglB,SADA,2BACA,CACAvmB,sCACAC,8HACAC,UAA2B1I,mDAAuD,CAAGA,iDAAqD,CAAGA,yBAI7I,CACAwI,wBACAC,oIACAC,UAA2B1I,2CAAiD,CAAGA,0BAG/E2I,yBACA,OACA4K,gCAAoD5jB,aAAgB,IAAGgoB,0BACvE1Z,OAAsCC,sBACtCqV,gCAAwD5jB,qBAAyBgoB,0BACjF5N,kBACA,EACA,EAAiB,YACjB9L,gCACA,EAEA,EACA,EACA8L,yCACAiD,6BAA6DjD,SAC7D,EAEA,IAEAD,wGACAC,eACAhM,eACAgM,WAAsB86D,iDACtB96D,kBACAA,2DACAzJ,yFACA,gCACAyJ,gCAEAwJ,0DACAxJ,UACA,EAAS,YACT9L,8BACA,EACA,EACA8L,UAAsB85D,gBAEtB95D,gCACAA,2BACA,6FACAA,2DACAzJ,yFACAiT,sBAAkCwwD,SAAS,CAC3CC,uCACAC,6CACAC,mBACAC,2CACAW,qBACAV,2CACSzsD,0BACT5N,gBACAA,uBACAnB,gBACAmB,6CAEAA,2BACAA,oBACA,EAAS,YACTA,2BAGAA,kBAFAwK,OACA,gCACA,0DAEA,gCACA,0DAGAA,OAIAA,CAEA,EACA,EAEAxK,kCACAuY,GACA1zB,sBACAmb,sBACAA,sBACA,GAAK,GAELA,iCACAA,sBACAA,sBACA,GAAK,EAEL,IAGAD,0NACAC,iBACAA,gCACA,0CACA,GAAKmG,mBACL,iEACA,GACAnG,aACAA,YAEAA,qBACAA,qBACAA,0BAEAA,qBAEAA,sBACAgsD,mBACAxiD,gBAAgChjB,8BAAmConB,0BACnEklB,IACA,EAAa,YACbA,GACA,EACA,EACAnb,WACAu0C,iBACAt0C,4BACAC,uBACA8U,oCACAvC,QACAnlC,mBACA,eACA,+CACA,wDACA,EACA2d,qBACA,eACA,+CACA,wDACA,IAIA5C,wBACAwJ,cAA0B5jB,qCAAyCgoB,0BACnE5N,8BACAA,gCACAA,kCACApa,oBACA6vE,sBACAD,kBACA3I,yBACAmO,sDAEAh7D,oCACA,EACA,EAEAwhD,MAWAxhD,WAA0B0gD,SAC1B1gD,2BACAA,qBACAA,0EAGAA,qBAEAA,gCACAA,0BACAA,oFAGAA,4BAvBAA,WAA0B0gD,SAC1B1gD,UAAyB/a,SACzB+a,qBACAA,0BACAwhD,gBACAA,mBACAA,mBACAA,0BAmBAxhD,oBAEAA,sBACAA,oBACAk7B,cAGAl7B,+BACAwJ,oCAAgD5jB,kEAA6EgoB,0BAC7H5N,qBAEA,6BAEAA,wBADAA,qCACA,WAGA,QAGAA,4BACAA,mCAEAA,iEACAA,kCAGAA,mCACAA,4DACAA,+CACAA,qDAEA,EACA,EAGAA,gDACAuY,IACA/O,+BAA+C5jB,OAAWgoB,0BAC1D5N,oBACAA,kCACAnB,sBACAo8D,yCACAj7D,0CAEA,EAGA,EAAa,YACb9L,OAAkCC,qEAClC,GACA6L,sBAGA,GAEAA,wDACAuY,IACAvY,sCACAi7D,UACAj7D,0CAEA,GACAuY,QACAvkB,uCACAgM,0CACAA,2CACAwJ,+BACA5jB,8BACAY,eACA00E,qDACiBttD,0BACjB5N,mBACA,IAGA,GAEAA,4DACAuY,WACAvY,2CACAwJ,mCACA5jB,8BACAQ,8CACawnB,0BACb5N,wBACA,GAEA,GAGAA,+BAA8Cpa,kCAE9Coa,6BACA2X,WACAC,gBACAC,kBACA8U,mCACAvC,QACAnlC,mBACA,SACA,kDADA,EAGA,EACA2d,qBACA,SACA,kDADA,EAGA,IAKA5C,sBACA2X,WACAC,wBACAC,wBACA8U,yCAGA3sB,6BACA2X,WACAC,oBACAC,oBACA8U,uBACAvC,QACAnlC,mBACA,SACA,sDADA,EAEA,EACA2d,qBACA,SACA,sDADA,EAEA,IAIA5C,iCACA2X,WACAC,gBACAC,mBACA8U,sBACAvC,QACAnlC,mBACA,SACA,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ACAC,uBACAC,sBACAA,WACA,IAGAwJ,6BAA6C5jB,iHAA2IgoB,0BACxL5N,mBACA,EAAa,YACb9L,OAAkCC,sEAClC,EACA,EAAS,YACTD,yCACA,EAAS,CACT,CAEA,IAGA6L,4GACAC,gCACA,0CACA,GAAKmG,mBACL,iEACA,GACAnG,8BAEAA,sBAAiCtE,oCACjCsE,mBACAA,kBACAA,gBACA,CAASgP,iCACT,CAASA,kCACT,CAASA,kCACT,CAASA,kCAGThP,oBACApC,OACA4L,2BAA2C5jB,iCAA4CgoB,SACvF,8DACApZ,iBACAwL,iBACA,EACA,EAEAA,mCACAuY,GACAvY,UAEA,GAEAA,6BACAuY,GACAvY,UAEA,GAEAA,sBACA2X,WACAC,wBACAC,wBACA8U,yCAGA3sB,oBACA,QACA,oCACA,0BACAq8D,2BACAC,oBACAC,YACAh5C,eACAi5C,YACAj5C,aACA,MACA0N,kBACAurC,0CAEAvrC,eACAsrC,sCAGAA,IACAtrC,6BACAurC,IACAvrC,kCAEArxB,QACAC,YACAC,kEACAC,kDACAC,iBACAA,0BACAA,iBACAA,kBACAA,wDACAzJ,uFACAyJ,kEACAzJ,4FAGAqH,2BAAyDhY,sBAA2Boa,2CACpFA,WACA,EACA,CACA,GACAmN,SACAq0C,wBAA2C,aAElC5iD,wBACTC,GACAmB,UACA,EACA,CAGA,IAEAD,wIACAC,cACAA,gCACA8K,+EACAA,8BAAwCiqC,OACxC,GAEA/0C,uBACAJ,QACAE,iEACAK,WAEA,EACAH,qBACAwJ,0DACAxJ,cACAA,gCACA8K,+EACAA,4BACAA,8BAAgDiqC,OAChD,EACA,EACA,EAEA/0C,wBACAglB,UACAvmB,wBACAC,gFACAC,UAAuB1I,2CAAiD,CAAGA,0BAClE2I,wBACTC,GACA2K,gCAAoD5jB,uBAA2BgoB,0BAC/E5N,uBACA,EAAiB,YACjB9L,gCACA,EAEA,EACA,EAEA8L,gBAEAA,sBACAA,0BACAA,eACA,EAEAA,yBACAA,0BACAA,gBACAA,WACA,EAEAA,oBACAA,kBACAy8D,WACAxC,WACAllB,wBACAkc,mBACAC,iBACAqJ,yBACAC,yBAEAj4D,QACAiU,eACAu+B,wBACA2nB,eAEA18D,eACA,EAEAA,uBACA,qBAEA4wC,cACAA,4BACAmE,kBACAkc,mBACAC,iBACAqJ,yBACAC,4BAGA5pB,KACApnC,6BAAuD5jB,SAAWgrD,YAGlEpnC,0CAEAhV,iBACAwL,YACAA,eACA,EAAS,YACT9L,8BACA,EACA,EAEA8L,2BACAJ,QACAC,YACAC,6EACAC,8CACAC,aACAA,iCACAqW,mFACAA,0EACA,GACArW,iCAEAA,gBAEAA,uBACAJ,QACAE,iEACAK,WAEA,EAEAH,qBACAA,iBACA+0C,wBACAkc,mBACAC,iBACAqJ,yBACAC,uBACAiC,aAEAz8D,eACA,EACAA,sBACAA,gBACAA,yBACAA,sCACA,EACAA,wBACAA,sBACA,EACAA,uBACAA,yBACAA,eACA,EACAA,yBACAA,YACAA,yBACAA,0BAGAA,iBAEAA,gBACAA,eACA,CACA,GACAG,UACAgN,SACA8sD,oBAAuC,mCAE9Br7D,uBACToB,8CACAqW,4FACAA,mFACA,EACA,EAEA,CAEA,IAGAtW,0IACAC,cAEAA,uBACAJ,QACAE,iEACAK,WAEA,EACAH,qBACAwJ,6DACAxJ,aACA,EACA,EAEAA,wBACAglB,UACAvmB,wBACAC,gFACAC,UAAuB1I,2CAAiD,CAAGA,0BAClE2I,wBACTC,GACA2K,mCAAuD5jB,uBAA2BgoB,0BAClF5N,uBACA,EAAiB,YACjB9L,gCACA,EAEA,EACA,EAEA8L,gBAEAA,sBACAA,gBACAA,yBACA,EAEAA,oBACAA,gBACAA,kBAA+By8D,8CAC/B,EAEAz8D,uBACA,sBAEA4wC,KACApnC,gCAA0D5jB,SAAWgrD,YAGrEpnC,6CAEAhV,iBACAwL,YACAA,eACA,EAAS,YACT9L,8BACA,EACA,EAEA8L,yBACAA,0BACAA,gBACAA,WACA,CACA,IAGAD,wIACAC,cAEAA,qBACAwJ,2DACAxJ,aACA,EACA,EACAA,uBACAJ,QACAE,iEACAK,WAEA,EAEAH,wBACAglB,UACAvmB,wBACAC,gFACAC,UAAuB1I,2CAAiD,CAAGA,0BAClE2I,wBACTC,GACA2K,iCAAqD5jB,uBAA2BgoB,0BAChF5N,uBACA,EAAiB,YACjB9L,gCACA,EAEA,EACA,EAEA8L,gBAEAA,sBACAA,0BACAA,eACA,EAEAA,oBACAA,kBAA+By8D,+CAC/Bz8D,eACA,EAEAA,uBACA,sBAEA4wC,KACApnC,8BAAwD5jB,SAAWgrD,YAGnEpnC,2CAEAhV,iBACAwL,YACAA,eACA,EAAS,YACT9L,gCACA,EACA,EAEA8L,yBACAA,0BACAA,gBACAA,WACA,CACA,IAEAD,yIACAC,gCACA,0CACA,GAAKmG,mBACL,iEACA,GAEAnG,sBACA2X,WACAC,wBACAC,wBACA8U,yCAGA3sB,kCACApC,QACA4L,+BAA2C5jB,mBAAuBgoB,0BAClE+uD,sBAGA,UACAd,MAHAZ,4EAIAA,gCAEAxkE,UACAqlE,IACAA,WACArlE,gCACA,YACAslE,UAEA,SACAA,eAGAC,cAEA,IAEAvlE,WACAwlE,IACAA,KACAxlE,gCACA,YACAolE,IAEA,SACAA,SAGAK,QAEA,GAEA,GACAjB,qBACAA,sBACA,GAEAr9D,YACAoC,mBACA,EAAS,YACTpC,YACA1J,OAA8BC,iFAC9B,EACA,EAEA6L,mCACAuY,GACAvY,wBAEA,GAEAA,yBACA,SACAA,sCACA48D,4BACA,GAOAh9D,QACAC,YACAC,0EACAC,wCACAI,UACAgN,SACA0vD,0BAA6C,wBAC7CC,qBACA,uCAA+Dl3E,mBAAwBgoB,QACvF,EACA2J,mBACA,+BAAuD3xB,mBAAuBgoB,QAC9E,EACAi/C,uBAA0C,yBAEjCjuD,wBACToB,wBACA,EACA,EAEAA,uBACAA,cACA+8D,gBAEA,EAEA/8D,2BACAglB,UACAvmB,wBACAC,sFACAC,UAAuB1I,2CAAiD,CAAGA,0BAClE2I,wBACTC,GACA2K,iCAAqD5jB,yCAA6D,IAAIgoB,yBACtH5N,wBACA,EAAiB,YACjB9L,gCACA,EAEA,EACA,CAEA,IAEA6L,sJACAC,mBACAA,8BAEAA,YACAA,cACAA,gBAEAA,UACAA,gCACA,2BACAA,8CAEA,GAGAA,sBACA/Y,UACA0wB,WACAC,kBACAC,kBACA8U,sBAGA3sB,uBACA2X,WACAC,gBACAC,kBACA8U,sBAGA3sB,kBACAA,UACAA,8BACAoB,wCACApB,gCAEA,GAGAwJ,+BAA2C5jB,kBAAuBoa,2CAClEA,UACA,EAAS,YACT9L,gCACA,EACA,CACA,IAEA6L,mGAEAzL,OAAwBi9B,oCACxBvxB,gBAGA1L,OAAwBi9B,oCACxBvxB,gBAGAA,sCACA+X,UACAxU,gCACAiT,gCACAwB,gBACAvB,YACAC,UACA5wB,0CAEA,CAGA,IAIAia,uHACA,mCACAoB,8BAIAnB,WACAy3B,cAAiC1Q,kCACjC3/B,cAAiC2/B,qCAIjC/mB,8BACA,uBAA6BsiD,OAC7B0a,EAEAroE,2CACAA,uCACAA,wCACAA,0CAIAA,gCACAA,8BACAA,gCACAA,4BAIAqoE,EACA,MAGA,EAGA,EAEAh9D,iCACA,eACAH,kBACAC,6EACAC,oCACAoN,SACA8vD,mBACA,YACAzzD,uBAA0D5jB,UAAegoB,0BACzE,yBAAiE,mBAAsBsvD,IACvF,GAGA,EAEA,EACArb,oBACA,WAAwCt/C,QACxC,EACA46D,yBACA,QACA,KAESv+D,MACT,EAEAoB,gCACA,eACAH,kBACAC,kFACAC,yCACAoN,SACA00C,oBACA,WAAwCt/C,QACxC,KAES3D,MACT,EAEAoB,gCACA,QACA,EAAgBuC,qCAChB,EAAgBA,uDAEhB,cACA,CAAaA,sCACbyV,yEACAlH,QACA,CAAqBrd,mCACrB,CAAqBA,uCAIrBijB,OACA,CAAanU,oDACbyV,2EACAlH,QACA,CAAqBrd,0CACrB,CAAqBA,sCAIrBskB,UACAxU,yBACAiT,6DACAwB,gBACAvB,UACA7wB,iBACA2c,iBACAmG,cACA00D,eAEA1mD,SACA5wB,0CAEA,EAEAka,+BACA,eACAH,kBACAC,8EACAC,qCACAI,UACAgN,SACA00C,oBACA,uBAA+Cj8D,UAAgBgoB,QAC/D,EACAyvD,qBACA,QACA,EACAC,yBACA,QACA,EACAC,wBACA,oCACA,KAES3+D,MACT,CACA,IAEAmB,sJACA,8BACAC,cACAhb,gBAA0B,wCAAyCE,IAEnE,QACAF,SAAoByO,+DACpB0S,YAAwB1P,sBAExBuJ,gBAEAA,eACAhC,iBACA4oD,iBACAC,wBACA2W,0BACA1W,mBACAkL,eACAyL,mBACAzW,kBACAC,0BAEAyW,cACAC,aACAC,cACAC,aACAC,eACAC,YACAC,cACAC,kBACAC,eACAC,gBACAC,8BACAC,gBACAC,iBAEApX,+IACAC,YACA,CAAczrD,kDACd6iE,yGAEA,CAAc7iE,qHACd,CAAcA,+EACd4C,QACAZ,qBACA8gE,kBAEA,CAAc9iE,qFACd,CAAcA,iCACd4C,QACAZ,qBACA8gE,gBAAoC/qE,yBAA2B,CAAGA,wCAElE,CAAciI,2GAIdsE,sBACA,0BAA8Cpa,qBAAuC,IAAGgoB,0BACxF1Z,OAA8BC,mDAC9B6L,kBACAA,oBACAA,UACA,EAAS,YACT9L,iCACA8L,UACA,GACAA,qCACA,EAEAA,2BACA9a,cACA8a,kBACAA,0BAEAA,kBACAA,oBAEA,EAEAA,sBACA,iCACA1L,kCACA,EAEA0L,yBACAy+D,aACA,wCACAjsB,GAGAxyC,gCACA,EAAS,KACT,EAEAA,wCACAA,YACA4vD,kCACAA,qDACA5vD,eACA,EAEAA,kBAEAA,0BACAA,mBACAA,gCACAA,uBACA,EACA,EACAA,iBAEAA,oBACAA,kDACAwJ,kBAA8BhjB,gDAAqDonB,0BACnF5N,cACAhb,gBAAkC,wCAAyCE,IAC3E8a,gBACA,EACA,EAEAA,kDACAA,UACA,GACAA,kDACAA,UACA,GAEAA,qBACAglB,UACAvmB,wBACAC,iFACAC,UAAuB1I,2CAAiD,CAAGA,0BAClE2I,wBACTC,GACA2K,mBAAuC5jB,UAAgBgoB,0BACvD1Z,OAAsCC,0BACtC6L,kBACAA,oBACAA,UACA,EAAiB,YACjB9L,iCACA8L,UACA,EAEA,EACA,EAEAA,gCACAA,wCACAA,UACA,EACA,EAEAA,+BACAA,uCACAA,UACA,EACA,EAEAA,+BACAA,uCACAA,UACA,EACA,EAEAA,4BACAA,qCACAA,UACA,EACA,CACA,IAEAD,2IACAC,aACAA,oBACAA,uCAAqDu5D,qBACrDv5D,cACAA,kBAEAA,2EACA,YAAoBrL,WAA0BA,KAC9C,WAEAutB,gBADAA,OACA1pB,oCAA6E,sBAE7E,sBAEA,CAEAwH,SAAoBtE,0BACpBsE,gBAEAA,gCACAuY,IACAvY,kBACAnb,gCACAoR,oBACA,GAEA,GAAK,GAUL+J,oBACA,2BACAwJ,gBAA4B5jB,UAAgBgoB,0BAC5C5N,YACA,GACAwJ,uBAAmCk1D,mGAAuI9wD,0BAC1K5N,mBACA,GACAA,kDAEA,EAEAA,yBACAA,UACA,EAEAA,yBACAA,gBACAA,UACA,EAEAA,kDACAA,UACA,GACAA,kDACAA,UACA,GAEAA,uBACAglB,UACAvmB,wBACAC,gFACAC,UAAuB1I,2CAAiD,CAAGA,0BAClE2I,wBACT,KACA,UACA/Z,yCACAoR,YACA,GAEA2H,QACA4L,wBAAwCoF,wBAA2BhB,0BACnEhQ,YACAoC,UACA,EAAa,YACbpC,YACA1J,iCACA8L,UACA,EAAa,CACb,EACA,EAEAA,mCACA,kBACA,4BACA,sDAEA,8BACA,gCAEA,8BACA,MAEA,8BACArL,kBAGA,GAIA,EAEA,EAGAqL,4BACA,SACAnb,yCACAoR,YACA,GACA+J,uCACAA,UACA,EACA,EAEAA,gCACAA,wCACAA,UACA,EACA,EAEAA,+BACAA,uCACAA,UACA,EACA,EAEAA,+BACAA,uCACAA,UACA,EACA,CACA,IAEAD,gIACAC,kBACAA,iCACAA,sBAAmC,kDACnCA,aACAA,YACAA,yBACAA,gBACAA,cAEAA,kBACAA,2CACAwJ,4BACAd,UACA,6BACAw4B,4BACSlhC,sCAETA,UACA,EAAS,YACT9L,8BACA,EACA,EAEA8L,sBACAA,2CACAwJ,4BAAwCd,WAAgB1I,8CACxDA,qBACAA,wBACA,EAAS,YACT9L,kCACA,EACA,CACA,IAEA6L,yHACAC,6CAAsE,gDACtEA,aACAA,cACAA,UACAA,WAEAA,kBACAhM,eACAgM,oBAAmCtE,4BAEnCsE,yBADAA,iBACAA,QACAA,eACAA,4BAEAA,qBAEAwJ,6BAAwCxJ,sCACxCA,UACA,EAAS,YACT9L,gCACA,EACA,CACA,IAGA6L,sKACAC,0DAAmF,iDAA6D1B,mBAQhJ,QAPA,yBACA,6BACA,uBACA,yBACA,oBACA,iBACA,0BACA,mBAEA,GACA0B,sBAA+BwxB,gDAE/BxxB,iBAEAA,aACAA,UACAA,UACAA,mBACAA,iBACAA,cACAs9D,aACAt9D,iCACAA,mBAGAA,kBAGA,SAMA,GALAA,iCACA2+D,WACA,GAGA9c,YACA,kCAEA7hD,gCACAA,kCACAA,UACAA,+BACAA,qCACA,0CACAA,uBAEA,EACA,CAEAA,2BACAo7D,IACAp7D,iCACAhF,YACAgF,uCAA+DhF,GAC/DmgE,UACAyD,gDACAC,qCAEA7+D,kBAEA,GACAA,sBAEA,EAEAA,oCACAuY,GACAvY,8BAEA,GAEAA,kCACAA,gCACAA,2BACA,EAEAA,qBACAA,qCACA,0CACA25B,uBAEA,GAGAnwB,WADA,yCACA,CACA5jB,QACAmjB,eACS,CACT+1D,yBACArmE,0BACAsmE,4BACAC,iCACSpxD,yBACT1Z,OAA8BC,8FAC9B6L,UACA,EAAS,YACT9L,4EACA,EACA,EAEA8L,kBACAA,qCACA,0CACA25B,uBAEA,GACAnwB,yBACA5jB,QACA8iB,iBACS,CACTo2D,yBACArmE,0BACAsmE,8BACSnxD,yBACT1Z,OAA8BC,sBAC9B,EAAS,YACTD,iCACA,EACA,CACA,IAIA6L,2EACA,mCACAoB,8BAGAnB,WACA2hD,iBAAoC5yB,OAAQowB,yBAC5CM,gBAAmC1wB,OAAQowB,+BAA+B,KAG1E,IAEAp/C,+MACA,sCACA,cAAgCgvB,OAAQowB,+BAA+B,yBACvE14D,qCAA+EsoC,OAAQowB,+BAA+B,uBACtH,GAAKh5C,mBACL,iEACA,GAGA84D,KACAC,sBACAD,kBACA,GAEAj/D,0DACAA,kCACAA,WAEAA,iBACA,CAASwxB,2BACT,CAASA,yBACT,CAASA,uBACT,CAASA,4BAGTlsC,oBACA0a,sBAAmCwxB,6BACnCxxB,kBAGA1a,qBACA0a,sBAAmCwxB,8BACnCxxB,kBAGAA,eACAA,UAEAA,sBACA,gBACAmB,8BAA+C+5B,sCAE/C,gBACAt7B,QACAC,YACAM,UACAL,4EACAC,iCACAC,aACAA,2BACAuY,GACAvY,WAEA,EACA,KACapB,wBACbwC,GACAD,8BAAuD+5B,wBAEvD,GAGA/5B,8BAA+C+5B,QAE/C,EAEAl7B,yBACAA,UACAA,+BACAA,gCACAA,0BACA,EACA,EAEAA,uBACAA,UACAA,+BACAA,gCACAA,0BACA,EACA,EAEAA,sBACAA,UACAA,2CACA,EAIAA,qCAEAA,+BACAA,qBACAA,wBACAA,eACAA,eACAA,kBAEA,SACAm/D,KACAC,IACAh7B,sBACA,UAAkCrV,OAAQowB,wBAAwB,sBAClE,KAEA,SACAluB,4BACAouC,eACA,SACAC,KACAC,KAEAF,+CACAG,iCACAD,qBACAA,uBACAA,sCACA,GAGAv/D,oBACAq/D,qCACA,+DACA,uBAGA,uBACA/gE,mBAA8C,cAC9CojB,WAEA1hB,uBACAs/D,QAA6C9tC,0BAC7C,wBAA+C6tC,QAC/CI,oBACAC,wBACAjgB,WACAkgB,UACAC,6BAGAC,yCAEAC,UACAC,UACA//D,sBACA,GAEAA,mCACAA,oBACAs/D,QAA6C9tC,wBAE7CxxB,iCACA,GACA8/D,uBACA,wBACArpE,gBACAA,8BACAA,2BACAA,iCACAA,sCACA,GACA0oE,QAA2Ba,kCAC3BhgE,sBACAA,wBAEA,GACA+/D,uBACA,gCACAtpE,wBACAA,iBACAA,gBACAA,8BACAA,2BACAA,iCACAA,sCACA,GAEAuJ,cACAA,2BAA0CggE,uCAC1ChgE,2BACAA,6BAEAA,+DACAo/D,MACAp/D,sBAEA,EAEAA,sBAEAA,0CACAuY,MACA1zB,gCACAA,gCACAgwD,mBACA,aACA70C,sCAGApC,QACA4L,iCAA6D5jB,MAAW,CAAEinE,sCAAmEj/C,0BAC7IhQ,WACA,EAAyB,YACzBA,YACA1J,kCACAiN,8BAAgE+5B,6BAAwC,CAAG5S,WAC3G,IAGA,EACA,EACA,GAAK,GAELtoB,gBAEAA,4BACA,6BACAigE,cACAjgE,qCACA,MACAnb,gCACAoR,kBACAwpE,kBACA,GAEAS,MACAtiE,QACA4L,iCAAqD5jB,SAAU,CAAEinE,gCAAwDj/C,0BACzH,wCACA,GAAiBpZ,iBACjBwL,sBACApC,WACA,EAAiB,YACjBA,YACA1J,kCACAiN,8BAAwD+5B,6BAAwC,CAAG5S,WACnG,IAGAnnB,8BAAoD+5B,6BAAwC,CAAG5S,WAE/F,EACA,EAEAtoB,6BACA,eACAH,kBACAM,UACAL,4EACAC,sCACAoN,SACAgzD,wBACA,qBACA,EACA15E,kBACA,QACA,EACAg5D,oBACA,iBACA,EACA2gB,uBACA,QACA,EACAze,qBACA,oBACA,EACA0e,yBACA,sBACA,EACAC,mBACA,wCAAgE16E,UAAUgoB,QAC1E,EACA2yD,yBACA,oDACA,KAGS3hE,MACT,EAEAoB,6BACAglB,UACAvmB,wBACAC,kFACAC,UAAuB1I,2CAAiD,CAAGA,0BAClE2I,wBACT,KAEA,UACA/Z,4CACAoR,GACAs2D,YAEA,GAEA3uD,QACA4L,0BAA0CoF,wBAA2BhB,0BACrE,wCACA,GAAapZ,iBACbwL,sBACApC,WACA,EAAa,YACbA,YACA1J,gCACA,EAAa,CACb,EACA,CACA,IAEA6L,0OACAC,kBACAA,gCACA,iEACA,GACAA,iBACAA,0BACAA,kBACAA,gBACA2hD,GACA3hD,WACAA,mBACAnb,gCACAoR,kBACA+J,gCACA,GACAA,iCAGAA,WACAA,kCACAA,mCAEAA,iBACAA,KAAgBtC,cAEhBsC,0BACAugE,sBACAvgE,4BACA,GAEAA,oCACAA,4CACAA,4DACAA,qDACA,GAEAA,eAEAA,wBACA,qBAIAA,2CACAA,kBACA,uBAEAA,2CACAA,kBACA,uBAGAA,kBACA,0BAbAA,kBACA,wBAcA,EAEAA,uBAEA,IADAA,iBACAA,0DAEA,SACA,gDACA,QACAnB,KAEA,GACAA,CACA,EAEAmB,oBACAA,sBACAA,uBACAA,wBACA,EAEAA,wBACAA,gBAEAA,uBACAA,yBACAA,6CACAA,+CACAA,mDACA,EAEAA,+BACAA,sBACA,EAEAA,0BAEA,sBACA,mCAA4D,sBAE5DgsB,gBACA,KAGA,CACA,gBACAnoB,wCACA7D,mEACAwgE,aACA38D,iDAGA7D,0CAEA,MAZAA,0CAYA,CACA,EAEAA,kBACAxH,4CACAwH,oBAIAA,sCACAA,4CACAA,oDALAA,aAOA,GAEApC,QACA4L,kCAA8C5jB,qBAA0Boa,sCACxEpC,YACAoC,oBACA,EAAS,YACTpC,YACA1J,iCACA,EACA,CAEA,IAGA6L,4EACAC,gBACA,oCACAmB,8BAIA,IAEApB,sHACAC,aAEAA,oBACApC,QACA4L,4CACA5L,YACAoC,YACA,EAAS,YACTpC,YACA1J,+BACA,EACA,EAEA8L,qBACAglB,UACAvmB,wBACAC,yFACAC,UAAuB1I,2CAAiD,CAAGA,0BAClE2I,yBACTC,GACA2K,kBAAkC5jB,OAAMgoB,0BACxC5N,UACA,EAAa,YACbpC,YACA1J,+BACA,EACA,EACA,EAEA8L,UAEA,IAEAD,uJACAC,YACAA,SACAA,SAAoBtE,iCACpBsE,wBACAA,6BACAA,6BACAA,iDACAA,iDACAA,8BACA,+BACAA,0BAEAA,cACAA,+CACAuY,GACAvY,kCACAtM,UACAsM,8BACAA,sCACAA,oCACA,EACA,GAGAA,gCACAuY,+BAEAvY,mBADAA,uBACAA,uBAEA,gBACA,GAGAA,oBACApC,QACA4L,eAA2B5jB,kBAAsBgoB,0BACjD5N,YACAA,gCACAA,qCACAA,qCACAA,yDACAA,yDACAA,sCACA,+BACAA,0BACApC,WACA,EAAS,YACTA,YACA1J,+BACA,GAEA0J,QACA4L,mBAA+B5jB,UAAoBgoB,0BACnDhQ,YACAoC,QACA,EAAS,YACTpC,YACA1J,+BACA,EACA,EAEA8L,kBACAA,aACAA,qBACAA,2CACAA,yBAEAA,uCACAA,uCACAA,8CACAA,kDACAA,mCACA,yDACAA,4BAEApC,QACAoC,aACAwJ,eAA+B5jB,iBAAsBoa,oCACrDpC,YACAoC,UACA,EAAa,YACbpC,YACA1J,6BACA,GAGAsV,oDACA5L,YACAuD,8BAAoDvb,SACpD,EAAa,YACbgY,YACA1J,6BACA,EAEA,EAGA8L,iBACApC,QACA4L,eAA2B5jB,iBAAsBoa,oCACjD,0BAA0Cpa,iBAAsB,IAAGgoB,QACnE,GAASpZ,iBACToJ,YACA1J,OAA8BC,sDAAmBuJ,cACjDsC,UACA,EAAS,YACTpC,YACA1J,4BACA,EACA,EAEA8L,yBACApC,QACA4L,qBAAiC5jB,2BAAiCgoB,yBAClEhQ,YACAoC,UACA,EAAS,YACTpC,YACA1J,gCACA,EACA,EAEA8L,qBAEA,OADAA,SACAA,yBAIAA,iCACAA,8BACA,GAGsE,GAAtEA,kCAAqD,eAAiBzH,SACtEyH,wCACA,IAVAA,4BACA,EAaA,EAEAA,sBACA2X,WACAC,gBACAC,oBACA8U,qCACA1lC,UACAmjC,QACAnlC,mBACA,SACA,mEADA,EAEA,EACA2d,qBACA,SACA,mEADA,EAEA,IAIA5C,wBACA,6BAEA3H,iCAIA,EAGA2H,yCACAk7C,sBACAt9C,QACA4L,mBAAmC5jB,UAAoBgoB,0BACvDhQ,YACAoC,QACA,EAAa,YACbpC,YACA1J,+BACA,GAEA,EAEA,IAEA6L,+EACAC,YACAA,QACAA,wBACAA,6BACAA,4BAEA,IAEAD,4FACAC,iBACAA,yBAEAA,wBACA,EAEAA,yBACAA,qBACAA,yBACAA,6BACA2uB,cACAA,sBACA3uB,2BACA,GAEAA,mCACAw5B,4BACAx5B,gCACAA,uCAEA,EACA,GACAA,uDAEA,EAEAA,2BACA,8EACA8K,YACAA,sCAGAA,QADAA,gCACAA,wBAEAA,kDACAA,2BAGA,GAEAA,CACA,EAEA,kDAEA9K,eADA2uB,GACAA,iBAMA3uB,qDACAA,eACA,GACA,IAEAD,iKACAC,SAAoBtE,0BAEpBsE,wBAAmC+G,6CACnC/G,QAAmBqQ,kBAEnBrQ,mBAEAA,qBAEA,eACAA,yBAEAgmD,qBACA,oBACA3tD,uBACA,6BACAA,EACc,sBACdA,EAEA5B,uCACA4B,EAEA5B,sCACA4B,EAGA,CAEA,GACA2tD,oDACAhmD,8BACA,EAEAA,qBACAA,gCACAA,WACA,EAGAA,yBACAA,eACAA,eACAA,WACA,EAEAA,8BACAuY,GACAvY,eACA,GAAK,GAGLA,4BACAA,YACAygE,sBACAzgE,gCACA,GACAA,kCACA,EACAA,mBAEAA,uBAA4C+uB,OAAQuwB,iBACpDt/C,iBAAsC+uB,OAAQuwB,kBAAgBhrD,OAAsBy6B,OAAQuwB,gBAC5Ft/C,mBAAwC+uB,OAAQuwB,kBAEhDt/C,qBACAA,aACAwJ,sCACAA,4BAA8C45C,iBAA0Bx1C,UACxEpZ,iBACAwL,mBACAA,WACA,EACA,EAEAA,4BACAA,iBACAA,UACA,EAEAA,yBACA,8BACA,uBACAjG,gEACA,iBACA/F,kCAEA,kBACAA,uCAEA,oBACAA,sCAEA+F,mBACAd,aAAkC+G,YAAiB,EACnD,MAEA3L,kEAAwF,CAAGE,SAAS,0BAAwBC,gBAC5HwL,UACA,EAAa,WACbA,UACA,EAEA,EAEAA,2BACAA,mCACAjG,oBAGA6F,QACAC,kBACAC,6EACAC,uCACAoN,SACAuzD,+BACA,4BACA,EACAxd,wBACA,qBACA,EACAyd,mBACA,QACA,KAEa/hE,wBACb,MACA,SACA/Z,wCACAoR,YACA,GACA8D,6BACA,CACA,EAEA,EAEAiG,uBACAJ,QACAC,kBACAC,6EACAC,uCACAoN,SACAuzD,+BACA,4BACA,EACAxd,wBACA,qBACA,EACAyd,mBACA,QACA,KAES/hE,wBACT,MACA,SACA/Z,wCACAoR,YACA,GACA,cACA04B,QACAnlB,kCAAqD,CACrDG,kBACAglB,OACAiyC,sBACiBhzD,0BACjB1Z,OAAsCC,oFACtC,EAAiB,YACjBD,+BACA,EACA,CACA,EACA,CACA,IAEA6L,wIACAC,YACAA,2BAA+Cnb,gBAC/Cmb,iBACAA,iBAEAA,sBACAgsD,mBACAxiD,gBAAgChjB,8BAAmConB,0BACnEklB,IACA,EAAa,YACbA,GACA,EACA,EACAnb,WACAu0C,iBACAt0C,4BACAC,uBACA8U,oCACAvC,QACAnlC,mBACA,eACA,+CACA,wDACA,EACA2d,qBACA,eACA,+CACA,wDACA,IAIA5C,4CACAuY,IACAvY,cACAA,uBACAA,mCACAw5B,6BACAjhB,aACAtzB,WACAA,8BACA,OACA+a,4BAGAA,gBAEA,EACA,EACA,GACA,GAAK,EAEL,IAEAD,gEACAC,0BACAA,cACAA,SAAoBtE,4BACpB,IAEAqE,uGACAC,OACAuD,mBACAs9D,kBACAh5D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0CACb,CAAaA,iDACb,CAAaA,kEACb,CAAaA,mEAEb8tE,oBACA52D,WACA62D,iBACK,CACLhrD,wBACA9Y,cACA2jE,qBACAvnC,gBACAzmC,SACA,CAAaI,+BACb,CAAaA,kCAEbguE,oBACA92D,YACK,CACL6L,0BACA9Y,cACA2jE,wBACAK,aACA,CAAajuE,6BACb,CAAaA,+BACb,CAAaA,oCAEbguE,mBACA92D,WACAg3D,gBACK,CACLnrD,8BACA6qD,2BACA3jE,cACAgkE,aACA,CAAajuE,6BACb,CAAaA,+BACb,CAAaA,oCAEbguE,mBACA92D,WACAg3D,gBACK,CACLnrD,2BACA4qD,mBACAC,uBACA3jE,cACAiN,YACK,CACL6L,0BACA6qD,uBACAO,WACAlkE,cACArK,SACA,CAAaI,oCACb,CAAaA,uCACb,CAAaA,8CAEbqmC,UACAnvB,YACK,CAELo9C,6BACK,CACLvxC,2BACA6qD,wBACA3jE,aACAiN,YACK,CACL6L,qCACA6qD,uBACA3jE,cACAgkE,aACA,CAAajuE,6BACb,CAAaA,+BACb,CAAaA,oCAEbguE,mBACA3nC,UACAnvB,WACAg3D,gBACK,CACLnrD,gCACA6qD,wBACA3jE,cACAgkE,aACA,CAAajuE,6BACb,CAAaA,+BACb,CAAaA,oCAEbguE,mBACA92D,WACAg3D,gBACK,CACLnrD,+DACA6qD,2BACAQ,kBAA6B,qCAAyCC,SACtEpkE,cACAkkE,WACAvuE,SACA,CAAaI,uBACb,CAAaA,uBAEbqmC,UACAnvB,YACK,CACL6L,2BACA6qD,wBACA3jE,aACAiN,YACK,CACL6L,oCACA6qD,2BACA3jE,aACAiN,YACK,CACL6L,mCACA6qD,mBACA3jE,aACAiN,YACK,CAELo9C,qBACK,CACLvxC,uCACA6qD,0BACA3jE,gBACAiN,YACK,CACL6L,qCACAqrD,kBAA6B,sCAAuF,KAAvF7hE,gCAAuFvM,OACpH4tE,wBACA3jE,cACAiN,YACK,CACL6L,+CACA6qD,yBACA3jE,gBACAiN,YACK,CACL6L,6CACAqrD,kBAA6B,qCAAqF,KAArF7hE,+BAAqFvM,OAClH4tE,uBACA3jE,cACAiN,YACK,CAELo9C,+BACA8Z,UACK,CACLrrD,mCACAqrD,SACAR,uBACA3jE,cACAkkE,WACAvuE,SACA,CAAamkB,uBACb,CAAaA,uBACb,CAAaA,uBACb,CAAaA,uBACb,CAAaA,uBACb,CAAaA,0BAEbsiB,WACAnvB,YACK,CACL6L,mCACAqrD,SACAR,2BACA3jE,gBACAiN,YACK,CACL6L,qCACAqrD,SACAR,6BACA3jE,gBACAiN,YACK,CACL6L,4CACAqrD,SACAR,yBACA3jE,cACAiN,YACK,CACL6L,sDACAqrD,SACAR,4BACA3jE,cACAiN,aAGA3K,eACAA,iCACAA,yBACA,GAGAA,sBACAA,WACA,IACAA,uCAIA,CAHA,MAGA,CAEAA,iCACAqW,YACA,GAEAxxB,4CACA,sBACAwxB,IAEAA,aACAA,UAGAA,QADA,oBACA,2BAEAA,UACAngB,YAGAD,EAGAogB,oBACAA,qCAGArW,2BACAqW,kCAEArW,4BACAqW,mCAEA,GACArW,iCACAqW,2BACAA,eACA,EACA,EAEArW,oBACApC,OACA4L,kBAAkC5jB,iBAAqBgoB,SACvD,8BACApZ,iBACAwL,YACA,EAAS,YAET,EACA,EAEAA,aAEAA,sBACAA,aACAA,8BAEAA,aADAA,qBACAA,qBAEAA,sBACA,sCAGA,eAIAA,8BAGAA,yCAEAA,oCACA,2BAEA,YAAsBrL,WAAiBA,KACvC,WAEA,SACA,sBACA+B,4BACAD,mBACAA,eACAC,mBACAA,QACA7R,sBACAwxB,WACArW,eAEA,EAEAqW,iBAEA,cACA,cACAA,kBAEA,oBACAA,UAEA,qCACAA,mBACAA,gBACAA,YAGAA,UAGA,kBACAA,UAEA,mBACAA,iBAGAA,+BACAA,uBAEAA,gCACAA,yBAGA,2FACAA,WAGA,CACA,GAAK,GAELrW,sBACA,SAEAA,gBACA,8BACAA,0BACAA,4BAEA,4CACAA,0BACAA,+CAEAA,cACAA,kCACAA,6BAGAA,0BACAA,6BAKAA,kCACAqW,YAGA0rD,cADA1rD,qCACA,KAGAA,QAGAA,mCACA0rD,gCAGA1rD,gBACA0rD,+BAEA,GACA/hE,gBACAA,cACApC,OACA4L,kBAAsC5jB,gBAAqBoa,mBAC3D,8BACAxL,iBACAwL,WACAA,eACAA,iCAAwDqW,YACxD,EAAa,YACbrW,UACA,GAGApC,OACA4L,qCACA,8BACAhV,iBACAwL,eACAA,iCAAwDqW,aACxDlV,gCAAqDvb,SAAU,CAAE0iC,WACjE,EAAa,YAEb,EAEA,CACA,IAEAvoB,2GAEAC,WACAA,sBACAA,8BACAA,oBACA,4BACApC,OACA4L,6BACA,6BACAhV,iBACAqK,sBACA,IACA9I,uCAIA,CAHA,MAGA,CACA,GACAiK,SACA,EAAS,YAET,EACA,EAEAA,WAEAA,gCAEAA,qBACAglB,UACAvmB,wBACAC,4EACAC,UAAuB1I,2CAAiD,CAAGA,0BAClE2I,yBACTC,GACAjB,OACA4L,mBAAuC5jB,UAASgoB,SAChD,0BACApZ,iBACAwL,UACA,EAAa,YACbA,UACA,EACA,EACA,EAEAA,mBACA,OAAwB85B,WACxBzjB,cACA2qD,4BACA3qD,iBACA2qD,+BACAphE,QACAC,kBACAC,oEACAC,oCACAoN,SACA60D,eACA,YACA,EACAC,gBACA,QACA,EACAC,kBACA,eACA,EACAC,oBACA,0DACA,EACAC,oBACA,2DACA,EACAp1E,gBACA,QACA,KAES4R,wBACToB,UACA,EAAS,YACTA,UACA,EACA,EAEAA,iBACAJ,QACAC,kBACAC,oEACAC,oCACAoN,SACA60D,eACA,YACA,EACAC,gBACA,OACApV,kCACAwV,oCACAC,eACAC,0BAEA,EACAL,kBACA,eACA,EACAC,oBACA,+CAA4EroC,aAAWlsB,QACvF,EACAw0D,oBACA,gDAA6EtoC,aAAWlsB,QACxF,EACA5gB,gBACA,QACA,KAES4R,wBACToB,UACA,EAAS,YACTA,UACA,EACA,EAEAA,sBACAJ,QACAC,kBACAC,yEACAC,oCACAI,UACAgN,SACA60D,eACA,YACA,EACAC,gBACA,OACApV,kCACAwV,oCACAC,eACAC,0BAEA,EACAL,kBACA,eACA,EACAC,oBACA,QACA,EACAC,oBACA,QACA,EACAp1E,gBACA,QACA,IAGA,CACA,IAEA+S,iKACAC,SACAA,SACAA,QACAhT,4BAEAgT,aACAA,aAEAA,gBACA2X,WACAC,mBACAC,qBACA8U,wBACA1lC,WAGA+Y,sBACA2X,WACAC,gBACAC,yBACA8U,4BACA1lC,WAGA+Y,4BACA,MAWApC,OACA4L,mBAVA0xC,EADAl7C,mBACA,CAAoB6sD,0CAEpB7sD,oBACA,CAAoBqiE,4CAGpB,CAAoBvoC,YAIpBlsB,SACA,gCACApZ,iBACAwL,YACA,GAEApC,OACA4L,gCACA,gCACAhV,iBACAwL,YACA,EACA,EAEAA,+BACAuY,yBAEAvY,OAkBAuY,oDACA3a,OACA4L,iCACA,kBACAhV,iBAEA,QADAguE,KACA7tE,IAAkCA,WAAaA,KAC/C,gCACA9P,oBACA29E,yBACA,CACAxiE,eACA,IA7BA,0CACA,uCACAuY,kBAEA,yCACAA,iBAGAA,oBAIAA,iEACAvY,oBAmBA,GAAK,GAGL,sFACA,kBACAA,aACA,GAEAA,sBACAhb,gBAA2B,OAAQyO,2CACnC0S,mBAA8B,wDAC9BnG,QACAA,oBAAiCvM,2BAEjCuM,yBACAhb,gBAA2B,OAAQyO,+CACnC0S,mBAA8B,wDAC9BnG,QACAA,yBAAsCvM,2BAEtCuM,sCACAhb,gBAA2B,OAASyO,8CACpC0S,mBAA8B,wDAC9BnG,QACAA,sBAAmCvM,2BAEnCuM,8CACAhb,gBAA2B,OAASyO,+CACpC0S,mBAA8B,wDAC9BnG,QACAA,6BAA0CvM,2BAE1CuM,kBACA,WAEA,4BACA,yHACA,YACAyiE,UACA,GAEAA,KACA7kE,OACA4L,gBAAoC5jB,SAAe68E,YACnD,uBACAjuE,iBACAwL,UACA,GAGApC,OACA4L,4BACA,uBACAhV,iBACAwL,UACA,EAAa,CAEb,CACA,IAGAD,iHAEAC,WACAA,sBACAA,8BACAA,oBACA,4BACApC,OACA4L,+BACA,6BACAhV,iBACAqK,sBACA,IACA9I,uCAIA,CAHA,MAGA,CACA,GACAiK,SACA,EAAS,YAET,EACA,EAEAA,WAEAA,gCAEAA,qBACAglB,UACAvmB,wBACAC,4EACAC,UAAuB1I,2CAAiD,CAAGA,0BAClE2I,yBACTC,GACAjB,OACA4L,qBAAyC5jB,UAASgoB,SAClD,0BACApZ,iBACAwL,UACA,EAAa,YACbA,UACA,EACA,EACA,EAEAA,mBACAJ,QACAC,kBACAC,2EACAC,0CACAoN,SACA60D,eACA,YACA,EACAC,gBACA,QACA,EACAC,kBACA,eACA,EACAC,oBACA,+CAA4EroC,aAAWlsB,QACvF,EACA5gB,gBACA,QACA,KAES4R,wBACToB,UACA,EAAS,YACTA,UACA,EACA,EAEAA,iBACAJ,QACAC,kBACAC,2EACAC,0CACAoN,SACA60D,eACA,YACA,EACAC,gBACA,OACApV,kCACAwV,oCACAC,eACAC,0BAEA,EACAL,kBACA,eACA,EACAC,oBACA,+CAA4EroC,aAAWlsB,QACvF,EACA5gB,gBACA,QACA,KAES4R,wBACToB,UACA,EAAS,YACTA,UACA,EACA,EAEAA,sBACAJ,QACAC,kBACAC,gFACAC,0CACAI,UACAgN,SACA60D,eACA,YACA,EACAC,gBACA,OACApV,kCACAwV,oCACAC,eACAC,0BAEA,EACAL,kBACA,eACA,EACAC,oBACA,QACA,EACAn1E,gBACA,QACA,IAGA,CACA,IAEA+S,0JACAC,SACAA,SACAA,QACAhT,4BAEAgT,aAEAA,gBACA2X,WACAC,mBACAC,qBACA8U,wBACA1lC,WAGA+Y,sBACA2X,WACAC,gBACAC,yBACA8U,4BACA1lC,WAGA+Y,4BAYApC,OACA4L,mBAXAxJ,mBACA,CAAoB6sD,0CAEpB7sD,oBACA,CAAoBqiE,4CAGpB,CAAoBvoC,YAIpBlsB,SACA,gCACApZ,iBACAwL,YACA,EACA,EAEAA,+BACAuY,yBAEAvY,OAkBAuY,oDACA3a,OACA4L,mCACA,kBACAhV,iBACA,YAAkCG,WAAaA,IAC/C,+DACAkK,cAEAmB,eACA,IA3BA,0CACA,uCACAuY,kBAEA,yCACAA,iBAGAA,oBAIAA,iEACAvY,oBAiBA,GAAK,GAGL,sFACA,kBACAA,aACA,GAEAA,sBACAhb,gBAA2B,OAAQyO,2CACnC0S,mBAA8B,wDAC9BnG,QACAA,oBAAiCvM,2BAEjCuM,yBACAhb,gBAA2B,OAAQyO,+CACnC0S,mBAA8B,wDAC9BnG,QACAA,yBAAsCvM,2BAEtCuM,sCACAhb,gBAA2B,OAASyO,8CACpC0S,mBAA8B,wDAC9BnG,QACAA,sBAAmCvM,2BAEnCuM,cAA0BvM,sBAAwB,CAAIA,wBAEtDuM,8CACAhb,gBAA2B,OAASyO,+CACpC0S,mBAA8B,wDAC9BnG,QACAA,6BAA0CvM,2BAE1CuM,kBACA,WAEA,4BACA,uGACA,YACAyiE,UACA,GAEAA,KACA7kE,OACA4L,kBAAsC5jB,SAAe68E,YACrD,uBACAjuE,iBACAwL,UACA,GAGApC,OACA4L,8BACA,uBACAhV,iBACAwL,UACA,EAAa,CAEb,CACA,IAGAD,2GAEAC,cACAA,sBACAA,8BACAA,cACAA,cAEAA,oBACA,4BACApC,OACA4L,gCACA,gCACAhV,iBACAwL,YACA,EAAS,YAET,EACA,EAEAA,WAEAA,gCAEAA,qBACAglB,UACAvmB,wBACAC,4EACAC,UAAuB1I,2CAAiD,CAAGA,0BAClE2I,yBACTC,GACAjB,OACA4L,sBAA0C5jB,UAASgoB,SACnD,4BACApZ,iBACAwL,UACA,EAAa,YACbA,UACA,EACA,EACA,EAEAA,mBACAmB,iCAA+Cvb,SAC/C,EAEAoa,kBACAmB,iCAA+Cvb,MAC/C,CACA,IAEAma,yJACAC,cAEAA,sBACAA,eACAA,gBACA,EAEAA,oBACApC,OACAxJ,OACAoV,mBAAuC5jB,kBAAsBgoB,SAC7DpE,wBAA4C5jB,kBAAsBgoB,WAElE,2BACApZ,iBACAwL,YACA,EAAS,YAET,EACA,EACAA,iBAGAA,sBACAA,cACAA,8BAEAA,aADAA,sBACAA,sBAEAA,uBACA,uCAGA,eAIAA,8BAGAA,6CACAuY,cACAvY,eAEA,GAEAA,uBACAA,cACAA,aACApC,OACA4L,mBAAuC5jB,iBAAsBoa,oBAC7D,+BACAxL,iBACAwL,eACAA,UACA,EAAa,YACbA,UACA,GAGApC,OACA4L,uCACA,+BACAhV,iBACAwL,eACAmB,iCAAuDvb,SAAU,CAAG0iC,WACpE,EAAa,YACbtoB,UACA,GAEA,EAEAA,mBACAJ,QACAC,kBACAC,yEACAC,wCACAoN,SACA60D,eACA,YACA,EACAU,oBACA,QACA,KAES9jE,wBACToB,UACA,EAAS,YACTA,UACA,EACA,EAEAA,iBACAJ,QACAC,kBACAC,yEACAC,wCACAoN,SACA60D,eACA,YACA,EACAU,oBACA,OAA4BC,6BAC5B,KAES/jE,wBACToB,UACA,EAAS,YACTA,UACA,EACA,EAEAA,qBACAglB,UACAvmB,wBACAC,4EACAC,UAAuB1I,2CAAiD,CAAGA,0BAClE2I,yBACTC,GACAjB,OACA4L,uBAA2C5jB,UAASgoB,SACpD,qBACApZ,iBACAwL,UACA,EAAa,YACbA,UACA,EACA,EACA,CACA,IAEAD,yGACAC,QACAA,uBACAA,YACAA,kDACAA,8CACAA,+CAGAA,4BACA,KACA,UACAuY,uBACA/N,uCAEA+N,qBACA/N,qCAEAxK,oBACAA,sBACAhM,qBACAA,sBACA,GAAK,GAGLgM,kBACApC,OACAoC,UACAwJ,oBAA0C5jB,cAAmBoa,iBAC7DwJ,qCACA,uBACAhV,iBACAwL,UACA,EACA,CACA,IAGAD,qFACAC,OACAuD,mBACAs9D,kBACAh5D,UAGA7H,wBACAA,4CACA,EAEAA,eAEAA,0CACAA,cACA,GAEAA,sBACA,iDACA,EAEAA,UACAiI,OACAw3C,YACAshB,UACAvlD,QAGAxb,sBACA2X,WACAC,gBACAC,qBACA8U,wBACA1lC,WAGA+Y,4BAEAA,4BACAA,oBAA6CsY,sBAE7ChkB,uEACA0L,6BACAA,yBACAA,iCAEAA,kBACAyhB,2BACAzhB,eACAA,kCACA,OAA6Bpa,qCAC7B,GAAiBugB,mBACjB,8BACA1P,uBACA,GAEA,EACA,EAEAuJ,mBACAA,uCACA,IAGAD,uFACA,oBAEAC,kBACAA,sBACAA,8BACAA,cAEAA,oBACApC,OACA4L,0BAA0C5jB,kBAAsBgoB,SAChE,gCACApZ,iBACAwL,gBACA,EAAS,YAET,EACA,EAEAA,WAEAA,gCAEAA,qBACAglB,UACAvmB,wBACAC,mFACAC,U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kBAKAA,aAJA,mHAEAA,6BAGAA,iBACA,CACA,IAEAD,2GACAC,mBACAvM,0CACK,CACLA,wCAGAuM,KACAtC,kBACAqL,UACAi6D,sBACA37C,SACA47C,mBACAC,aACA3tB,YACAhyC,SACAM,WACAs/D,SACAC,kBAGApjE,qBACA,4BACA,6EAGA,sEAKA,GAFA,0EAEA,iDAEA,8BACA,+BAGAA,qBAEA,QACA,EAEAA,8BACA,iBACAA,mBACAvM,0CACa,CACbA,wCAEAuM,2BAEA,eACAA,mBACAvM,0CACa,CACbA,mDAEAuM,+BAEA,oBACAA,mBACAvM,mDAEAuM,8BAEA,GAEAA,uCACA,iBACAA,cACA,GAEAA,sBACAgsD,mBACAxiD,gBAAgChjB,8BAAmConB,0BACnEklB,IACA,EAAa,YACbA,GACA,EACA,EACAnb,WACAu0C,iBACAt0C,4BACAC,uBACA8U,kDACAvC,QACAnlC,mBACA,eACA,+CACA,4EACA,EACA2d,qBACA,eACA,+CACA,4EACA,IAIA5C,qBACAwJ,cAA0B5jB,qBAAyBgoB,0BACnD,8CACA5N,gBACApa,oBACA4wB,sEAEAxW,oBACA,EACA,EAEAA,sBACAA,qBACA,EAEAA,kBACA,SACA2uB,KACA5lB,KAEA,YACA,iBAEA,gCAAqD,cAIrD,GAHA,wBACAo6D,wBAEA,qDAEA,6BAMA,GALAzkE,cACA6E,gBACAM,qBAGA,4BACA,cACAnF,qBACA,YAGA,OAFAA,cAEA,SAEA,6BACA,qBACAA,qBACA,MAEAiwB,cAGAA,2CAEA,8BACAA,yBAFAA,8BAIA3uB,gBACA2uB,aAEA3uB,eACAtB,YAEA,kCACAqK,KACA,SACA,uBAGA,GAFArK,yBAEA,4BACA,cACAA,qBACA,YAGA,OAFAA,cAEA,MAGAiwB,eAGAA,6CACAA,+BAEA3uB,gBACA2uB,aAEA3uB,eACAtB,YAEA,kCACAqK,KACA,MAEArK,cACA2kE,UAA4Bxd,gBAG5Bl3B,cACAA,yBACAjwB,WAEA,kCACAqK,MAGA,SACAA,IACAu6D,WAEA1lE,OACA4L,kBAA2C9K,mBAA6BkP,SACxE,0BACApZ,iBAEAN,MADA6U,EACA,CAAkC5U,8DAElC,CAAkCA,6EAClC,EAAS,EACT,CACA,GAEA,CAtscA,GCDA,WACA,aAEAstB,8BACAA,wCACA3tB,cACAo9C,sBAAoCp9C,cAAaA,KACjD,GAIA,wBACA,cACA,SACAoG,4BACA,0BACA,EACA,CAGA,oBACA,oBACAqpE,EAEA,oBACA7vE,EAEA,WAGA,YAIA6vE,CAEA,EAEAC,gBA4BA,QACA,GAxBAv+D,gBACAA,cACAA,gBACAA,sBACAA,mBAEAu7B,kBACAv7B,oBACA,iCACAA,gBACAw+D,mBAEA,EAEAjjC,WACAv7B,oBACA,wBACAA,gBACAw+D,GAEA,GAIAjjC,iBACA,yBACA,QACAkjC,cAAmD,oBACnD,MACA,SACAA,cAAmD,UACnD,MACA,QACAA,cAAmD,oBACnD,MACA,SACAA,cAAmD,oBACnD,MACA,SACAA,cAAmD,oBACnD,MACA,QACA,wDAgCA,GA5BAljC,sBACAmjC,cACA,YAA0BhvE,WAAcA,IAExC,GADAsQ,YACAA,+BACA,SAEA,QACA,EAGAu7B,aACAmjC,cACA1+D,aAEA,QADA2+D,KACAjvE,IAAyBA,WAAgBA,IACzC,MACA,GAEAivE,oBACA,cAHAA,mBAMA,QACA,GAIAD,MACA,0BACA,uBACA,EAAS,YAET1+D,cADAy+D,UAGAC,UAOA1+D,mBACAu7B,qBACAv7B,+BAIAu7B,sBACAv7B,wBAGA,GAAS,GAETA,mBACA,MACAu7B,kBACAlV,sBACA,iCACA,EAAS,YACTrmB,aACA,GAAS,GAGTA,cAEA,EAIApgB,4LACA67C,oCACA,OACAz7B,SACA87B,aAEAsF,cACAnzB,oQACAs9B,wBACA,OACAgI,oBACA,0BACAqrB,8BACAC,QAEA7+D,QACAjH,qBACAmpD,cAGAL,wCACAF,oCACA8K,oBACA7K,kDACA8K,0CACAoS,kDACAC,+CACAC,+CACArS,4CAGAC,sBACAC,2BACA9K,sCACA+K,6BACAC,cACAkS,4BAEA5U,kBAQA6U,cAEAC,0BACAn/D,WACA,GAGAA,0BACA,yDACA,CACA,EAEA,EAEA,GAEAy7B,oCACA,OACAz7B,SACA87B,aAEAyP,sBACA,OACAgI,oBACA,gBACAtlC,iBAKAA,GADAA,GADAA,GADAA,GADAA,mBAAoD,OACpDhZ,eAAoD,OACpDA,yBACAA,0BACAA,wBAEA+K,YACAkkC,cAEA,EAEA,EAEA,GAGAzI,0CACA,OACAz7B,SACA87B,aAEAyP,sBACA,OACAgI,oBACA,gBACAtlC,iBAKAA,GADAA,GADAA,GADAA,GADAA,mBAAoD,OACpDhZ,eAAoD,OACpDA,yBACAA,0BACAA,wBAEA+K,mDACAkkC,cAEA,EAEA,EAEA,GAEAzI,kCACA,OACAz7B,SACA87B,aAEAyP,sBACA,OACAgI,6BACAvzC,wBACAA,sBAGA,OACAvJ,cACAu2D,8BACAC,0BACAmS,wBACAC,eAGAC,KACA/jC,SACA+jC,mBAEAA,IACAt/D,iCACAA,sBACAA,iBAAmDkzC,2BAGnD,mCACAlzC,qCACAA,yCAIA,EAEA,EAEA,GAGAy7B,kCACA,OACAK,aACAsF,cACAnsC,WACAgZ,wNACAjO,SACA2F,qBACA44D,MACA,EAEA,GAEA9iC,sCACA,OACAK,aACAsF,cACAnzB,iSAMAjO,SACA2F,qBACA44D,MACA,EAEA,GAEA9iC,qCACA,OACAK,aACAsF,cACAnzB,+MACAjO,SACA/K,WACA0Q,qBACA44D,MACA,EAEA,EAOA,CA3WA,GCAA,WACA,aAEA3+E,8CACAkb,6KACAzL,WAEA0L,SACAA,UAEAA,kDAEAnb,oCACAskD,uBACAnpC,uBAEA,EACA,GAEAA,oBACApC,QACA4mE,WAA+BhnE,YAAqBoQ,0BACpDhQ,YACAoC,sBACA,EAAS,WACTpC,WACA,EACA,EAEAoC,sBACAmB,4BAA0Cvb,YAC1C,EAEAoa,oBACAykE,6BACA,EAEAzkE,qBACAykE,+BACA,EAEAzkE,mBACAykE,+BACA,EAEAzkE,qBACA,sCACA,EAEAA,oBACA,qCACA,EAEAA,qBACA,sCACA,EAEAA,qBACA,sCACA,EAEAA,oBACA,IACAusD,KACA1nE,8BAA8C0nE,eAC9CiY,iBAAqCjY,QAAS3+C,0BAC9ChO,QACAE,qEACAC,mCACAF,YACAM,UACAmT,iBACAnG,SACAu3D,iBAAwC,OAAQC,0BAChDC,sBAA6C,OAZ7C,CAY6C,EAC7CC,sBACA,mBACAjnE,QACA,SACA+/B,KAEA94C,sCAEAq9B,sBACAqqC,aACA5uB,eAGA,GAEA6mC,WADA,CAA2CjY,gCAC3C3+C,yBACAhQ,YACAoC,UACA,EAA6B,YAC7BpC,YACA1J,gCACA8L,UACA,EACA,CACA,IAGA,EAAS,WACTpC,WACA,EAGA,EAEAoC,oBAEAJ,QACAE,qEACAC,mCACAF,YACAM,UACAmT,iBACAnG,SACA8vD,mBAAsC,UACtC2H,sBAAyC,OATzC,CASyC,EACzCE,sBACA,mBACAlnE,QACA,SACA/Y,8BAA8D0nE,eAC9DiY,YAAgDjY,wHAA2J3+C,yBAC3MhQ,YACAoC,UACA,EAAyB,YACzBpC,YACA1J,gCACA8L,UACA,EACA,CACA,IAGA,EAEAA,oBACA,uCACA,EAEAA,uBACA,8BACA,EAEAA,wBACA,+BACA,EAEAA,uBACA,sCACA,EAEAA,wBACA,qBACA,EAEAA,qBACA,mBACA,oBACA,CACA,EAEAA,yDAEAA,8BAIA1L,cAHA,CACAmkC,0BAGAjkC,gBAGA,QAFAuwE,GAEAA,oBACA,mBAIAA,SAIAA,EAUA5jE,4BAAuDvb,GADvDm/E,OARAN,eACAjwE,iBACA2M,4BAA2Dvb,SAC3D,EAAqB,YACrBoO,6CACA,EAMA,EAAa,YACbA,+DACA,EACA,CAEA,IAEA+L,iIAGA,GAFAC,UAEA1L,6BAGA,CAGA,QAFAywE,GAEAA,oBACA,mBAIAA,SAIAA,EAUA5jE,4BAA+Cvb,GAD/Cm/E,OARAN,eACAjwE,iBACA2M,4BAAmDvb,SACnD,EAAa,YACboO,6CACA,EAMA,MA1BAmN,mBA2BA,IAEApB,4PAiBA,GAfAC,yDAEAA,SACAA,SACAA,4BACAA,mFAEAA,6BACAkiB,2EACAliB,2BAEAkiB,6GACAA,qEACA,EACAliB,YACA,yBACAA,4BACA,YAAuBrL,mBAA4BA,IAEnDqL,8BAGA,CACAA,mBACAA,oCACA60C,UACA70C,UAEA,GAGAA,oBACApC,QACA4mE,WAA+B5+E,6BAA8CgoB,0BAG7E,GAFAhQ,YACAoC,4BACA,wBACA,YAA+BrL,WAAeA,IAC9CqL,uBAGAA,wBACA,EAAS,WACTpC,WACA,EACA,EAGAoC,kDACAk7C,2BAEAl7C,UAEA,GAEAA,wBACApC,QACA4mE,WAA+B5+E,UAAoBgoB,0BACnDhQ,YACAoC,mBACAA,kBACA,EAAS,WACTpC,WACA,EACA,EAEAoC,wBACAykE,qCACA,EAEAzkE,sBACAykE,qCACA,EAEAzkE,uBACA,6CACA,EAEAA,0BACA,gCACA,EAEAA,2BACA,iCACA,EAEAA,0BACA,yBACA,EAEAA,yBACA,2BACA,EAEAA,qBACApC,QACA,SACA/Y,8BAA8C0nE,eAC9CiY,oBAAwC5+E,cAAmB,CAAE2mE,QAAS3+C,yBACtEhQ,YACAoC,UACA,EAAS,YACTpC,YACA1J,iCACA8L,UACA,EACA,EAIAA,wBACAJ,QACAE,2EACAC,iDACAF,YACAM,UACAmT,iBACAnG,SACA8vD,mBAAsC,UACtC+H,+BACA,mBACApnE,QACA,SACA/Y,8BAA8D0nE,eAC9DiY,oBAAwD5+E,cAAmB,CAAE2mE,eAAqB3+C,yBAClGhQ,YACAoC,UACA,EAAyB,YACzBpC,YACA1J,iCACA8L,UACA,EACA,CACA,IAGA,EACAA,gCACAA,uBACA,SACAnb,8BACAogF,gBACAA,sDAGAL,IAEA,GAEAhlE,QACAE,uEACAC,sCACAF,YACAM,UACAmT,iBACAnG,SACA8vD,mBAAsC,UACtC2H,sBAAyC,UACzCM,sBACA,mBACA,SACArgF,8BACA+D,SACA,GACA/D,sCACA+D,UACAmpB,6CACAA,yDAEA,EACA,CACA,IAGA,EAEA/R,6BAIA1L,cAHA,CACAmkC,0BAGAjkC,gBACA2M,mBACA,EAAa,YACbnN,+DACA,EACA,CACA,IAEA+L,wFAEAC,8DAEAA,2BACA,iCACAoiB,YACA,wBACAA,CACA,EAEA,IAEAriB,kLACAC,YACAA,mBACAC,SACA,EAEAD,sBAEA,OACA+I,SAEAxiB,QACA1B,+BACAkE,kBACAxC,gBACA,GAEAyZ,aACAwkE,oBAAwC5+E,sBAA4BW,6BACpE,SACA1B,8BACAkR,yBACAovE,yDACA,GACAtgF,sCACAK,cACAA,mBACA,GACA8a,aACAklE,IACA,EAAS,YACThxE,yCACA8L,YACA,EACA,EAEA4kE,GAEA5kE,aAEA,IAEAD,iIACA,sHACAC,YACA,YAAkBrL,WAAiBA,IACnCqL,eAA4BvM,8BAK5B,IAFAuM,gBACAA,cACArL,2BAAsCA,6BAAkCA,IACxEqL,wBAEA,+BAAsCrL,6BAAkCA,IACxEqL,sBAGAA,YACAA,OAAkBsjB,OAClBtjB,YAEAA,iCACAA,kBAGA,QAFAolE,IACAC,IACA1wE,IAAsBA,gBAAwBA,KAC9C,WACAyM,iCACAgkE,IAEAhkE,0EACAikE,GAEA,CACAA,gDACAD,+EACA,GAAK,GAELplE,oBACAC,SACA,EAEAD,mBACAglE,gBACA/kE,SACA,EAIAD,cAEAA,YAAuBgkB,mCACvBhkB,yCASA,IACAD,2HACAC,YAGAA,oBACA,MAEA,mDAEA,YADA9L,OAAkCC,+CAIlC,SAIA,GAHAikB,UACAxZ,0GAEAA,EAGA,OAFAkmE,IAGA,CACA7kE,SACA,EAEAD,YACAslE,WACAC,mBACAt3E,SACAu3E,sBAOA,IACAzlE,yHACAC,aACAA,yHAGA,QADAqH,IACA1S,IAAmBA,qBAA4BA,KAC/C,qBACA8wE,GAA6BxgF,0IAC7B+a,kBACA,mBACA,wCACA0lE,KACA1lE,gBAAkC/a,iJAGlC,QADA0gF,KACAxnE,IAAuBA,WAAqBA,KAC5C,WACAynE,KACAC,KACAC,mBAEAC,iBACAC,sBAEAF,GAJA5jD,mBAIA6jD,KACAD,OAEAF,8BADA5lE,YACA,YACA6lE,KACAF,KACAF,YAEAG,gDAGAA,gDAEA5lE,gBAAiC/a,8FACjC,CAEAwgF,cACAp+D,GACA,CAGArH,oBACA,MAIA,QAFAimE,KACAC,KACAvxE,IAA2BA,mBAAwBA,IACnDyjB,4CACA6tD,MACA7tD,sEACA8tD,MAGA,MAGA,OAFAhyE,OAAkCC,yEAClC8L,UAIA,KAEA,YADA/L,OAAsCC,wDAGtC0wE,IAGA,CACA5kE,SACA,EAEAD,YACAkP,UACA+tD,kBAMA,IAsIAn4E,uKAMA,MALA,CAKAqhF,sBACA,gBACA,eACA5iE,qCACAzD,6DACA2W,UAA6Bo2C,4CAC7B/5C,UACAC,YACSve,iBACToJ,QACA4mE,oCACA5mE,YAEAE,YACA,EAAa,YACbF,YACA1J,+BACA4J,UACA,EACA,EAAS,WACTA,UACA,GACAA,SACA,EAEAqoE,qBACA,gBACA,eACA5iE,sCACAzD,6DACA2W,WACA3D,UACAC,YACSve,iBACToJ,QACA4mE,qCACA5mE,YACAE,WACA,EAAa,YACb5J,+BACA0J,YACAE,WACA,EACA,EAAS,WACTA,WACA,GACAA,SACA,EAEAqoE,qBACA,gBACA,eACA5iE,sCACAzD,2DACA2W,UAEAlU,QACAsqD,2BACAnvD,eAESlJ,iBACToJ,QACA6Y,UACA+tD,cAA2C52D,yBAC3ChQ,YACAE,WACA,EAAa,YACbF,YACA1J,+BACA4J,UACA,EACA,EAAS,WACTA,UACA,GACAA,SACA,EAEAqoE,wBACA,gBACA,kBACA5iE,sBACA7E,wFACSlK,iBACToJ,QACA,SACA/Y,8BAAkD0nE,eAClDiY,aAAqCjY,QAAS3+C,yBAC9ChQ,YACAE,WACA,EAAa,YACbF,YACA1J,kCACA4J,WACA,EACA,EAAS,WACTA,UACA,GACAA,SACA,EAEAqoE,uBACA,gBACA,kBACA5iE,sBACA7E,6GACSlK,iBACToJ,QACA,SACA/Y,8BAAkD0nE,eAClDiY,YAAoCjY,QAAS3+C,yBAC7ChQ,YACAE,WACA,EAAa,YACbF,YACA1J,gCACA4J,WACA,EACA,EAAS,WACTA,UACA,GACAA,SACA,EAGAqoE,wBACA,gBACA,kBACA5iE,sBACA7E,2HACSlK,iBACToJ,QACA,SACA/Y,8BAAkD0nE,eAClDiY,aAAqCjY,QAAS3+C,yBAC9ChQ,YACAE,WACA,EAAa,YACbF,YACA1J,mCACA4J,WACA,EACA,EAAS,WACTA,UACA,GACAA,SACA,EAEAqoE,sBACA,gBACAC,SACA,4CACAA,MACAA,WAEAA,eACAA,OAEA,GAGA11B,QACAntC,wBACAzD,6DACA2W,UACAtR,YAES3Q,iBACToJ,QACA,SACA/Y,8BAAkD0nE,eAClDiY,gBAAwCjY,4CAA+D3+C,yBACvG/oB,8BAAsDogF,oBACtDrnE,YACAE,WACA,EAAa,YACbF,YACA1J,wCACA4J,WACA,EACA,EAAS,WACTA,UACA,GACAA,SACA,EAEAqoE,wBACA,gBACA,kBACA5iE,sBACA7E,wFACSlK,iBACToJ,QACA,SACA/Y,8BAAkD0nE,eAClDiY,gBAAwCjY,2BAA8B3+C,yBACtEhQ,YACAE,WACA,EAAa,YACbF,YACA1J,uCACA4J,WACA,EACA,EAAS,WACTA,UACA,GACAA,SAEA,EAIAqoE,sBACA,gBACA,eACA/xE,OACAoV,wBAAwC04D,0CAAwCt0D,SAChF42D,aAAqC5+E,YACrC4O,iBACA,WACA6xE,OACAzoE,YAEA,SACA,UACA/Y,8BACAwS,gBACA,GAGAxS,8BACAwS,QAGA,uBAEAivE,YACAA,2BAIAA,YACAA,0BAVAA,YAYAA,oBACA,GAEA51B,QACAntC,uCACAzD,8DACA2W,UAAiC4vD,2BACjCvzD,UACAC,WAEA,GAASve,iBACT,SACA3P,wCACA,aACA0hF,UAEA,eACAA,UAEA,GACA3oE,QACA4mE,aAAqC5+E,SAAa,CAAG4gF,mBAAuB54D,yBAC5EhQ,YAEAE,WACA,EAAa,YACbF,YACA1J,iCACA4J,WACA,EACA,EAAS,WACTA,UACA,GAEAA,SACA,EAEAqoE,4BACAz1B,QACAntC,uBACAzD,+DACAgT,UACAC,UACA0D,cACSjiB,iBACTgwE,iBAAyC5+E,SAAY6wB,4BACrDviB,OAAkCC,8BAClC,EAAa,YACbD,wCACA,EACA,EACA,EAEAiyE,6BACAvoE,QACA4mE,aAAiC5+E,UAAYgoB,0BAE7ChQ,YACA8yC,QACAntC,yBACAzD,gEACAgT,UACAC,UACA0D,WACAtW,WAEA,EAAS,YACTvC,YACA1J,8CACA,EACA,EAEAiyE,qBACA,eACA3B,aAAwC5+E,UAAYgoB,0BAEpDhQ,YAEA,SACA6oE,KACAxuD,iCACA,WACApzB,8BACA4hF,SACA,GACAC,gBAGAh2B,QACAntC,iBACAzD,+DACAgT,UACAC,UACA0D,UAAiClO,oBACjCpI,YACa3L,iBACb,sDACAiiB,OACA+tD,UAA6C5+E,SAAY6wB,6BAIzD,mBACAviB,OAAsCC,wBACtC,CACA,EAAiB,YACjByJ,YACA1J,gCACA,EACA,EACA,EAAS,YACT0J,YACA1J,8CACA,EACA,EAEAiyE,qBACA,WAEAz1B,QACAntC,uBACAzD,6DACAgT,UACAC,UACA0D,UACA7wB,QACA+gF,uBACA/iE,cACAyjB,iBAES7yB,iBACToJ,QACA4mE,UAAkC5+E,SAAY6wB,4BAC9C7Y,WACA,EAAa,YACbA,YACA1J,gCACA,EACA,EACA,EAGA,IAEApP,mMACA,OAEAqhF,yBACA,aACA,kCACA,IACAS,+DACA,sBACAA,CACA,EAGAT,+BACA,gBACA,SACA,wBACA3B,mBAAuC5+E,uBAAuCgoB,0BAC9EhQ,YACAiB,WACApF,gCAEAoF,QACA3K,OAAkCC,4BAGlCD,OAAkCC,8BAClC0yE,qBAEA,EAAS,YACTjpE,YAEA1J,OAA8BC,+BAD9B,mCAC8BuJ,cAC9B,EAAK,CACL,EAEAyoE,kCACA,qBACA1vD,GACAsU,UACA/jB,QACAphB,gCACAkhF,aACAC,aACA7/C,QACAxpB,UACAspE,SACA,qCACA,0CACA,yBACA,sCACA,wCAIAniF,8BACA+Y,QACA6K,0BACA7K,YACA6Y,cACA,EAAa,YACb7Y,YACA1J,uDACA,EACA,GAEA0J,QACA4mE,WAA+BhnE,mBAA8BoQ,0BAC7D6I,WACA,SAaA,GAZA5xB,8BACAqiC,eACAzQ,gBACAgwC,cACAwgB,MAEA,GACAA,IACAxwD,SAEAA,yDAEAniB,8BAGA,QAFAywE,GAEAA,cACA,mBAIAA,SAIAA,GAOAtuD,UACAA,gBAPAA,cAEAA,oBADA,CAAoC,mEAEpCA,kBAMA,CAEA,mBACAi6B,QACA5wC,mEACAgT,UACAC,UACA0D,YAEA,GAASjiB,iBACT,gBAEAiiB,KACA3Y,cAGAF,QACA4mE,QAAoCjiE,0BAAyCqL,0BAC7E6I,UACA7Y,YACAE,WACA,EAAiB,YACjBF,YACAE,WACA,IAGAA,0BACA,mBAAsCopE,wBACtCtpE,QACA4mE,UAA6C5+E,SAAgB,CAAGmR,UAAe6W,QAC/E,GAAapZ,iBACb,gBACAd,4FACAwkD,wBACAxkD,mGACAwkD,YACAxkD,4CACAwkD,aACAxkD,qEAEAkK,YACA1J,OAAkCC,OAClC,EAAa,YACbD,yCACA0J,WACA,EACA,EACA,EAGAuoE,4BACA,IAIAgB,EACAC,EALA14C,oBACA4N,sBAEAA,wBAGAz3C,wCACAmP,eACA,sCACAknD,gBACAisB,SAEAjsB,uCACAA,aACAksB,OAEA,GACAviF,gCACA,mBACAmP,gBACAozE,YAGAD,oBACAjgD,IAEA,IAGA,qBACAzQ,GACArJ,SACApG,QACAo1C,qBACAx2D,4BACAkhF,aACAC,aACA7/C,QACAxpB,UACAspE,SACA,qCACA,0CACA,yBACA,sCACA,wCAIAniF,8BACA+Y,QACA6K,0BACA7K,YACA6Y,cACA,EAAa,YACb7Y,YACA1J,uDACA,EACA,GAEA0J,QACA4mE,WAA+BhnE,mBAA8BoQ,0BAC7D6I,WACA,SAaA,GAZA5xB,8BACAqiC,eACAzQ,gBACAgwC,cACAwgB,MAEA,GACAA,IACAxwD,SAEAA,yDAEAniB,8BAGA,QAFAywE,GAEAA,cACA,mBAIAA,SAIAA,GAOAtuD,UACAA,gBAPAA,cAEAA,oBADA,CAAoC,mEAEpCA,kBAMA,CAEA,mBACAi6B,QACA5wC,mEACAgT,UACAC,UACA0D,YAEA,GAASjiB,iBACT,gBAEAiiB,KACA3Y,cAGAF,QACA4mE,QAAoCjiE,0BAAyCqL,0BAC7E6I,UACA7Y,YACAE,WACA,EAAiB,YACjBF,YACAE,WACA,IAGAA,0BACA,mBAAsCopE,wBACtCtpE,QACA4mE,UAA6C5+E,SAAgB,CAAGohB,8CAA2D4G,QAC3H,GAAapZ,iBACb,gBACAd,4FACAwkD,wBACAxkD,mGACAwkD,YACAxkD,4CACAwkD,aACAxkD,qEAEAkK,YACA1J,OAAkCC,OAClC,EAAa,YACbD,yCACA0J,WACA,EACA,EACA,EAEAuoE,yBACAz1B,QACA5wC,oEACAgT,UACAC,UACA0D,aACSjiB,iBACToJ,QACA4L,oDACA5L,YAEA1J,MADA,uBACA,CAAsCC,6CAGtC,CAAsCA,uBAEtC,EAAa,YACbD,yCACA0J,WACA,EACA,EACA,EAGAuoE,+BAIA,IAKAgB,EACAC,EANAlgD,GAAqB,kEAErBwH,oBACA4N,sBACAA,wBAGAz3C,wCACA,sCACAq2D,gBACAisB,SAEAjsB,uCACAA,aACAksB,OAEA,GACAviF,gCACA,oBACAuiF,YAGAD,oBACAjgD,IAEA,IAGA,OACAhhC,UACAk2D,qBACAp1C,QACAkgB,SACAxpB,QACAspE,SACA,qCACA,0CACA,yBACA,sCACA,uCAEAphF,iCAIAgY,iBACA,gBACAvJ,4CAA6D9N,kBAAkBiO,iBAC/E6yE,iBACA,EAAS,YACTA,WACA,GAEAA,2BACA,QACA,4CACA,qBACAC,KACAziF,qCACA,gCACAq2D,QACAosB,UAGAA,YAEA,IAEA,wBACAC,KACA1iF,qCACA,gCACAq2D,QACAqsB,UAGAA,YAEA,GAEA,GAEA1iF,gCACA,mBACA0iF,oBACArgD,KAEAogD,oBACApgD,IAEA,GAEAzQ,uBACAA,kBAEA7Y,qBACA6Y,yBACAA,0BACA5xB,mCAEA,iCAEAe,IACAgY,QACA6K,0BACA7K,YACA6Y,cACA,EAAqB,YACrB7Y,YACA1J,uDACA,GAEA,GAEA0J,iBACA4mE,WAA0ChnE,mBAA8BoQ,QACxE,EAAS,YACT,4BACA1Z,wDACAE,UACA,GAASI,iBAEToJ,qBAEA6Y,WACA,SAaA,GAZA5xB,8BACAqiC,eACAzQ,gBACAgwC,cACAwgB,MAEA,GACAA,IACAxwD,SAEAA,yDAEAniB,8BAGA,QAFAywE,GAEAA,cACA,mBAIAA,SAIAA,GAOAtuD,UACAA,gBAPAA,cAEAA,oBADA,CAAoC,mEAEpCA,kBAMA,CAEA,eACA3W,mEACAgT,UACAC,UACA0D,YAEA,EAAS,YACT,4BACAviB,4CACAE,UACA,GAASI,iBACT,gBAEAiiB,KACA3Y,cAGAF,QACA4mE,QAAoCjiE,0BAAyCqL,0BAC7E6I,UACA7Y,YACAE,WACA,EAAiB,YACjBF,YACAE,WACA,IAGAA,0BACA,mBAAsCopE,wBACtCtpE,QACA4mE,uBAA0D5+E,SAAgB,CAAGM,qDAAoE0nB,QACjJ,GAAapZ,iBACb,gBACAd,4FACAwkD,wBACAxkD,mGACAwkD,YACAxkD,4CACAwkD,aACAxkD,qEAEAkK,YACA1J,OAAkCC,OAClC,EAAa,YACbD,yCACA0J,WACA,EACA,EACA,GA3hBA,OA6hBAuoE,CACA,IAEArhF,sDACA,SAEA,kDACAqhF,+BACAA,UACA,GAEAA,qCACA,IACArkD,EADAwK,KAEA,KACAxK,iBACAzhB,QACAisB,sBAKA,CAJA,MAGAA,yCACA,CACA,iBACA,SAEA,SACAq/B,KACA6b,KAGA,uCAEA,mBACAjpD,WACA15B,8BACAwhB,uBAAkDgiD,UAAmBtyD,GACrE,GAGAyxE,UAIA7b,QAA6BtD,kBAE7B,GACA/7B,qBACAA,uBACAA,kCACAA,CACA,EACA65C,CACA,IAEA7nE,mCACA,mBACA,MACA,SACA,oBACA,WACA,sBAEA,SACA,uCACA,mBACA+H,YACAxH,kCAIAA,8BAEA,GACAA,YACA,CACA,GAEA/Z,4GACA,OACAwrB,QAGA9c,oBACAi0E,+BACA,gFACAhuE,sBAEA,EACA,EAEAjG,6BACAlO,aACAk/E,4CACAxwE,eACAR,UACAqL,+BACAA,qCACArL,yBACA,EAEA,GACAA,UACA,EAEAA,oBACA,iCACA,GAxBA,OA0BAc,uCACAd,oBACA,GAEAA,CACA,IAEAuM,0FACAza,YACA0a,4BACAA,kCAGAA,8BACAA,kCAEA,IAKAlb,iDACA,iCAA6C,CAC7Cy5B,SACA94B,aACAK,oDAEA4hF,WACAjiF,aACAK,yCAEAm3E,SACAx3E,aACAK,2DACAC,YAEA46E,SACAl7E,aACAK,gDACAC,YAEA4hF,WACAliF,aACAK,mCACAC,YAGA6hF,cACAniF,aACAK,0CACAC,YAEA6C,QACAnD,aACAc,QAAqBX,WAGrBs7C,QACAz7C,cACAK,oCAEA+hF,qBACApiF,cACAK,2CAEAssB,OACA3sB,cACAK,uEAGAgiF,WACAriF,cACAK,mCAGAiiF,eACAtiF,cACAK,0CAEAuH,WACA5H,cACAK,qCAEAkiF,kBACAviF,cACAK,gDAEAmiF,kBACAxiF,cACAK,gDAEAyF,UACA9F,cACAK,oCAEAoiF,UACAziF,cACAK,oCAEAqiF,UACA1iF,cACAK,oCAEAsiF,WACA3iF,cACAK,qCAEAuiF,cACA5iF,cACAK,yCAEAwiF,QACA7iF,cACAK,qCAEAyiF,eACA9iF,cACAK,sCAEA0iF,kBACA/iF,cACAK,qCACAC,YAEA0iF,QACAhjF,cACAK,oCAEA4iF,iBACAjjF,cACAK,6CAEA6iF,gBACAljF,cACAK,4CAEA8iF,eACAnjF,aACAK,wCAEA+iF,iBACApjF,aACAK,2CAGA,GA2BA,CA79DA","names":["angular","factory","JSON","map","item","e","jsondata","rsp","itemfieldDefs","configService","Work","get","method","transformResponse","Mfhd","id","workMfhds","url","isArray","bvMfhdSvc","Prefilter","query","cache","number","Item","getCnBrowse","params","view","branch","ccode","workItems","getByBarcode","nextBarcode","op","relinkMfhd","target_mfhd_id","create","transformRequest","put","delete","cancelTransfer","checkin","setStatus","clearStatus","offlineCheckout","testRecall","recall","Dlso","metadata","summary","bib","Hold","confirm_fill","print_slip","send_to","getForPatron","testPlace","cancel","requeue","resume","suspend","ItemCheckinNote","item_id","update","getForBib","ItemStatus","getAll","Issue","itemIssues","issue","testRenew","renew","LostItem","refund","remove","defer","claimReturned","getforPatron","lostitem","AccountType","accounttype","getList","PatronAccount","sendAlert","emailReceipt","getReceipt","redistributeCredits","WorkAttributes","Branch","getVendorList","getAcqVendors","getAcqVendorAccounts","getAcqVendorCountries","deleteAcqVendorAccount","saveAcqVendorAccount","getAcqPurchaseOrders","getAcqPurchaseOrderLines","getAcqSubscriptionPOLines","pairAcqSubscriptionPOLine","getSerialInstances","getSubscriptions","getNcipAgencies","Vendor","getLedgers","getLedger","deprecateAll","purgeAll","getClassificationTable","setClassificationTable","Ledger","getPriceModelConfig","PatronGroup","getGroup","rmGroupMember","addGroupMember","rel","updateRelationship","PatronRegistration","getApps","rejectApp","approveApp","submitApp","Patron","getBranchList","getSuEligibleList","getRoleList","generateSuTicket","makeTemplatePatron","getSocialMediaAccounts","mergePatron","deletePatron","test","Catalog","exportSearch","ImportScript","execute","deleteAll","deepCopy","getParameters","getRuns","ImportScriptParameterSet","ImportScriptlet","approveAll","ImportScriptRun","forAll","ActionLog","getFieldMap","getStats","purge","Login","linkSocialInit","linkSocialFinalize","loginSocialInit","loginSocialFinalize","lostPass","Periodical","search","getSerialEditions","predict","generateEdition","deleteWithItem","Subscription","SerialEdition","SerialInstance","saveAll","claim","SerialPatternTemplate","SerialChronologyTemplate","SerialSequenceTemplate","ItemTemplate","WorksList","ItemList","getEntries","saveWithEntries","saveFromSearch","saveFromUpload","validateEntries","saveMetadata","setName","modifyItems","deleteItems","ItemListEntry","Message","send","MessageTemplate","tree","getTemplateCodes","applyTo","MessageType","getWithParameters","setBranchTemplate","replaceTemplateSet","Fee","getFromLostItem","waive","hasBody","Payment","setReallocate","reverse","webPay","gateway","paypalCustToken","createWebpayOrder","Report","schedule","getRun","deleteRun","exportRunAsBatch","exportRunAsItemList","ReportTag","CronJob","CronScript","CircControl","getScopes","CircPolicy","CircRule","testTuple","CircTermset","getDates","CircTermDate","RecallRule","MarcValidationRuleset","checkSyntax","checkAllSyntax","NcipAgency","Worldcat","getOpenUrlResolve","getEntriesSearch","getCollectionsSearch","SessionApiMetadata","generateError","traceUser","options","MarcDisplayTmpl","getDefaults","svc","value","s","now","d","W","this","sampleUri","console","self","alertService","msg","$q","$http","userService","headers","then","errorCode","patronData","i","hold","checkout","doAuth","prompt","collection","stat","catch","finally","init","patronHolds","patronCheckouts","_login_listener_dereg","xml_str2json","ArrayOfLibraryDocumentSummary","fieldDefs","fieldDef","customFields","_interfaceVisibility","results","rec","fd","val","parseInt","staff","itemClass","defaults","defaultVal","dayjs","publication_date","a","b","numberSorter","useDisplay","sortField","sortFunc","items","textual_holdings","visibleItemCols","location","suppressed","mfhd","has","expand","mfhds","reN","aA","bA","bN","naturalSorter","homebranch","addDummyMfhd","public","edit","edit_id","addMfhdItem","bvItemSvc","showMfhdCols","n","itemCopy","length","Object","constraints","include_item","selectionProgram","selectedItem","module","wl","$sceDelegateProvider","ready","$timeout","AvailableFacetValues","returned","cIndex","Value","returnedFacets","default_service_proxy_auth","default_service_proxy_auth_domain","window","log_level","service_proxy_auth","service_proxy_auth_domain","mkws","that","$rootScope","inputQ","xformItem","replace","queryStringToHash","query_string","vars","k","opacConfig","config_copy","jQuery","forEach","g","inGroup","KOHA","_indicatorLabels","_labelOverrideMap","f","subfields","spec","loaded","hasMarcLabel","safeMarcspec","getMarcLabel","ind","labelFromMarcSpec","ind_val","field","noteIsHidden","loggedin","is_staff","is_infomart_vendor","has_staff_masthead","can_place_holds","savedSearches","clientSession","acl_fq","q_user_id","q_authenticated_user_id","q_authenticated_user_details","q_any_user_details","lastExecutedSearch","loginDlgOpened","observable","setTicket","_data","_firstPromise","noauth_ok","fines","total","total_accruing","issues","overdue","holds","pending","waiting","transiting","user","authRequired","type","$location","loading","modalService","deferred","p_details","data","catalogue","access","x","millis","startTimer","filter","missingData","$injector","heading","message","buttons","result","rv","promises","p_searches","p_all","curSession","_userPromise","config","collectionToken","_failCount","getSession","password","opt","_sessionQ","expires","$log","$uibModal","backdrop","templateUrl","controller","$scope","$uibModalInstance","windowClass","size","auth_rsp","error","patron","updatePatron","loginAuth","session","clear","promise","timeout","$","onExpires","onIdle","onActivity","login","rv_deferred","$state","v","enc","old_prefs","prefs","setAllPrefs","perm","o","permitNode","toSend","submit","configObj","ermsg","printWindow","fname","document","unique","string","escapeRegExp","m","name","bibcache","maxAge","capacity","holdingscache","option","pendingPromise","bibObj","$resolved","embeds","bibid","bibPromiseCache","useroptions","depunct","bibdata","default_cover_id","title","title_ext","isbn","marc","uri","format","content","language","geo","records","uuid","opac_hold_policy","isSerial","opac_hold_ok","lvl","author","publicationDate","createdDate","loadDate","isDeleted","isSuppressed","itemsAreRenewable","currentSearch","lastSearch","clearCurrentSearch","onSearchChange","scope","geoShape","status","srch","terms","el","wasHighlit","sound_map","beep","warn","alert","play","audio","authcache","authPromiseCache","authObj","fq_authtype","fq_linked","sort","pager","hits","searching","done","fq","solrParams","authid","rcn","marctext","used","typecode","numResults","bibs","dlsos","exportFlag","selected","fullview","index","viableDLSOs","uIndex","AddFlag","dIndex","sv","bool","lists","dirty","p1","p2","listdata","all","to_update","allshelves","pubshelves","myshelves","comment","list","shelf","bibService","key","keyOrObj","thing","bind","things","async","contentType","success","outcome","courses","reserve","reviews","mine","text","kwApi","requests","user_cache","user_id","rating","mytags","tag","q","statusmap","ASKED","responsedata","suggestions","submitted","ent","curId","str","err","context","alerts","class","link","linkCallBack","t","uuids","tryDlsoThumbnail","preferred_thumb","getUUID","curr856","ARCHVIEW","deferred_uuid","regExImg","clImageURI","regExCL","regExImgOD","odImageURI","regExOD","sizes","l","f024","upc_param","file_ext","gbsdata","img","gbs_timeout","gbs_queue","requested_isbn","covers","trigger_gbs_load","cover","service","stateListener","prevAction","borrowernumber","biblionumber","holdingbranch","placeHold","reserveforothers","maybeGetPatron","patronSelect","resolve","bibids","patronid","bvSearchToPick","modalInstance","browseShelf","controllerAs","modalClose","startItem","$promise","delBibs","savedSearch","shelfnumber","addToList","recordView","response","last_solr_index","indextime","hotkeyModal","global","marced","kwHotkeySvc","active","hotkey_actions","contexts","action","desc","dialog","icon","label","alertClass","notify","help","dlgData","btnDef","ret","pullInterval","retryInterval","failInterval","running","lastPollTime","messageIdCache","setPollPref","Math","message_poll_interval","delay","elapsed","curTime","$sce","total_count","start","count","resp","filter_ids","param","sparams","qParams","inherit","newLimits","values","keys","fqArr","limitStr","defaultSolrParams","thisyear","decadeThreshold","interval","pubdateIntervals","y","pubyear_intervals","lexidx","startrange","_itemFacetConstraints","resultsDeferred","facet","split","r","item_constraints","sortby","rows","newSearch","clone","SearchQuery","score","facetField","searchQuery","logID","q_str","$stateProvider","onEnter","onExit","header","footer","formConfig","templatePromise","template","dialogClass","backdropClass","model","backdropClick","noveListParams","version","profile","contentTypes","seriesTitles","backlink","objpath","similarTitles","similarSeries","goodReads","similarAuthors","relatedContent","appeals","lexile","kohaBibs","noveListContentByKohaIsbn","findBacklinks","novelistTitles","novelistISBNs","novTitle","solrQuery","primary_isbn","searchList","qsofar","defType","embed","findPrimaryIsbnFromManifestation","matched","isbn_to_bibid","bibid_array","unlinked","unlinkedTitles","linkedTitle","resolvedTitles","resolved","NoveListContent","fetchlist","requested_isbns","ISBN","nContent","contentQs","ourContent","toBacklink","reduce","processContent","allContent","p","succ","logEvent","description","formdata","inputs","fileRequired","uploadButtonText","hasFormdata","percent","postdata","encoded","Upload","tainted","ledgersPromise","scriptsPromise","vendorsPromise","vendor","vendors","display","ledgers","isExport","maxItems","valueField","labelField","script","kwFileUploadSvc","instructions","json","ledger_id","vendor_id","obj","import_script_id","parameters","newVal","oldUpload","responseValidator","methods","$window","defaultHotkeyMap","specialKeys","shiftNums","bareModifier","event","character","modif","special","possible","registerKey","keymap","register","deregister","keyAction","siteKey","siteName","userIsAuthenticated","userId","userRatings","baseUrl","ratingsCache","site_id","set","comments","post","ratings","user_name","site_name","filename","blob","navigator","urlCreator","def","win","path","child","isReady","Error","ctor","peg$subclass","peg$SyntaxError","literal","escapedParts","classEscape","any","end","other","descriptions","j","describeExpected","describeFound","root","SyntaxError","parse","peg$result","peg$FAILED","peg$startRuleFunctions","peg$startRuleFunction","peg$c3","peg$c5","peg$c10","peg$c12","peg$c16","peg$c17","peg$c19","peg$c21","peg$c22","peg$c23","peg$c25","peg$c26","peg$c27","peg$c31","peg$c34","peg$c38","peg$c39","peg$c40","peg$c44","peg$c46","peg$c48","peg$c49","peg$c51","peg$c52","peg$c53","peg$c55","peg$c56","peg$currPos","peg$posDetailsCache","line","peg$maxFailPos","peg$maxFailExpected","peg$silentFails","details","column","s1","s2","s0","s3","s4","concat","peg$c7","peg$parseorExpr","peg$fail","expr","parens","peg$c6","peg$c8","input","s5","s6","s7","term_min","term_max","inclusive_l","inclusive_r","peg$c54","peg$parserange_operator_exp","range","peg$c13","peg$c15","peg$parseparen_terms","fieldq","peg$c14","peg$parsequoted_term","peg$parseproximity_modifier","term","quotes","boost","peg$c28","peg$c36","peg$parsefuzzy_modifier","peg$c29","peg$parsedecimal_or_int_exp","peg$c41","peg$c42","peg$c47","expected","found","peg$buildStructuredError","endPosDetails","offset","peg$computeLocation","Array","join","termlist","getTerms","qterms","parsed","subqueries","query_array","news","entry","date","order","parsed_subqueries","oldlimits","itemtype","eformat","loc","ln","audience","pubyear","from","to","lexile_i","geoscale","rangeSearch","available","suppress","queries","instr","num","add","rm","ensemble","toString","opened","create1","create2","load1","load2","dateFormat","open","kohaSearchSvc","datetime","time","limits","ebsco","indexData","koha","collapsed","currentSelection","selected_view_type","resetSource","stateParams","rawSearchTerms","src","kohaDlg","ngInclude","close","scopevars","advancedFacets","component","bindings","kwLuceneParser","ctrl","bib_ratings","sq","authoritySvc","limfield","fieldDisplay","vmatch","facetValue","facetInfo","facetName","limval","otherAuthvals","rcns","operators","newTokens","clearAOIAndDontSearch","subq","newSize","targetedActions","can","do","selectedBibList","cartService","editcatalogue","item_list","bib_promises","types","acqListSearchAPI","defaultName","email","btnClass","recipient","subject","BibBatchService","awDailyExportService","newFacetsObj","merging","extantFacet","newFacetsArr","facetfield","ratingService","use_default_filter","triggerSearch","fromState","doFetch","itemid","reload","$anchorScroll","searchparams","resultsperpage","includefacets","meta","ebscoService","indexDataApi","$stateParams","recIndex","failed","flatUUID","bIndex","currUUID","currGUIDE","UUIDS","DLSOViewerService","GUIDE","record","loopDLSO","currRecord","successCB","queryMetadata","tempUUIDS","domain","mapComptrollerSvc","processData","dataType","mapCast","triggerFailed","render","bibTitle","thumbnail","marcXML","marcID","dlso","completeRecordProcessing","deletionRecords","deletions","theSame","firstProgram","itemids","foundType","callslip_check","check_callslip_perms","passedRecord","currRec","show","dereg","on","authorSearch","kwNoveListSvc","youMayAlsoLike","kohaTagsSvc","kohaXISBNSvc","editions","bib_q","pubs","form","attr","textarea","tags","request_type","rfi","holdings","$event","kohaCallslipSvc","kohaReviewSvc","selectizeConfig","searchField","closeAfterSelect","openOnFocus","plugins","onDropDownOpen","hasFq","filtercfg","unlim","pf","allowEmptyOption","placeholder","filt","clearMinimal","fq_arr","public_view","docked","focus","visible","messageService","redirectOnFail","notifications","ACCEPTED","ORDERED","RECEIVED","AVAILABLE","REJECTED","codes","kohaSuggestSvc","aoi","aoiMethod","ignoreExtent","curSearch","opts","frontendOpts","userdata","modalresult","tools","bib_ids","compressed","packageInfo","coverageString","dlsoIndex","marcRecords","exportData","packaged","finalPath","marcPoster","REQUEST","SERVICE","VERSION","CRS","COVERAGE","BBOX","OUTPUTFORMAT","PACKAGETYPE","FORMAT","METADATAFORMAT","METADATASCHEMA","allCoverages","allFormats","covIndex","formatHash","coverages","hash","formIndex","sortfield","shelfname","shown","kohaListsSvc","cc","c","is_public","viewingOwnList","showEmail","bibsToRemove","cssClass","circulate","code","waitOnData","patronholds","myHoldsByBib","pickup_branch","for_patron","syncUser","override","notes","nv","item_level","test_ok","tested","holdData","anyItemHeld","truthy","manyholdsperbib","pendingHold","numShown","originalSort","username","token","callback","kohaCourseSvc","department","course_name","course","re","instructorTest","items_attached","bib_is_on_hold","aqorders_link","suggestions_link","subscription_link","patron_tags","patron_reviews","output","bvDownloadSvc","fetchData","getResult","updateCounts","accruing","dueSoon","isNaN","waitingHolds","month","year","proxyrelations","dashboard","callslips","hide","bib_list","acqlists_switch","acqsubs","illrequests","messages","jobs","async_jobs","social","page","history","pagelength","resolvedUser","currentIssue","confirm","allOk","go","renewal_promises","cnt","renewedCnt","odpatron","reserveId","checkoutDate","checkoutDateToLocal","current","selector","holdDateToLocal","successmsg","errmsg","can_act","allowedActions","suspendParam","action_promises","ats","patron_id","fee","fees","unallocated","getAcct","enable","_ACCRUING","callslip","userRequiredDetails","mustRequire","len","userTimezoneOffset","borrowers","modify","syncLists","expired","filled","canceled","bib_title","holdHistoryDays","cutoff","E","allHolds","withinCutoff","approved","ordered","received","rejected","listScope","filtered","cardnumber","jobService","once","jobArgs","dir","vA","catsources","dispB","dispA","basic","masthead","which","qf","ok","group","metafacets","sources","mf","hostname","filterName","default","filterDef","fdef","created_date","extension","load_date","modified_date","product_format","product_size","product_type","src_file_info","valid","fcn","newConfig","doc","detail","MarcRecordDocumentationUri","uploadProductService","passed","kwSocialMediaSvc","mode","idType","idValue","searchType","newState","byIndex","coverage","linkerurl","article_url","coverage_enum","collection_name","collection_url","isDirect","merged","select","lines","rft","prevState","prevStateName","search_type","qp","fv","hq","gen_article_link","totalResults","itemsPerPage","startIndex","collection_uid","provider_uid","provider","wrapper","formatStr","symbol","out","scale","newItems","extractValueToCompare","isDuplicate","withTime","dateFilter","wday","names","xx","ordnames","ordname","pl","every","lhs","rhs","hms","mapTitle","defaultMap","branchcode","fallbackVal","enumchron","identity","mapDef","reverseOrder","sortPredicate","descending","predicate","$parse","constant","comp","isPrimitive","v1","v2","t2","t1","tc","se","money","isHourly","atoms","syncTemplateCache","subfieldJoin","fieldJoin","filterEach","filterLast","tagspec","reorder","delimiter","target_subfields","target_subfields_len","targettag","authvalcat","attrs","field_html","directive","getTags","includeTags","dlsoSummary","targetSummary","restrict","solrbib","drg","append","collapseEl","$label","childrenWithContent","$node","cfg","$clicker","classname","defaultBlockClassname","$root_el","block_has_label","appendTo","block_has_data","$fieldset","$joinspan","renderedFields","classnames","interpolator","$el","$bibEl","sflist","$filter","sf","i2","i1","marcBibSpec","fieldClasses","html","flabel","$field","linkedRecord","$linkEl","$sfat","$sfow","marc856","linktext","detailLink","collapseFields","showMoreText","hideText","$collapsedFieldset","$collapsedContainer","searchTrigger","qtxt","normalizeQuery","wrapInner","processor","runFieldProcessor","renderFieldsetHtml","staffDetail","tmpl","targetbibid","dModel","prevCallback","hreff","currModel","css","fadeIn","mousemove","kohaOptions","require","$compile","showFqDropdown","menuOnFocus","prefilter","simple","masthead_search_form","bvAsyncOnreadySvc","$queryInput","parent","fqs","bail","maxEntries","authval","inputClass","displayValueType","valueClass","transcludedModel","clearTranscluded","displayTranscluded","validTranscluded","reorderable","emptyOption","transclude","element","onSelect","limitTo","facetOption","conj","not","newParams","addLimit","facet_option","newLimit","rmLimit","refreshValues","minValue","maxValue","userMax","userMin","floor","ceiling","setLimits","minDate","maxDates","max","kohaBib","imgSize","serviceLink","detailsLink","itemId","overlayText","coverEl","image","maxTitleLength","itemcallnumber","kwCoverImgSvc","loadDefaultImage","updateImage","tagterm","sortspec","otherway","sortedDir","kohaToggleAll","ngModelCtrl","$attr","newValue","$element","each","elem","draggable","handle","resizable","modalPos","initEvent","eventType","bibId","update_time","index_time","targetInputEl","find","auth","idx","cslip","serial","syspref","orderby","creserves","tabdef","setTabFocus","patronSearch","catSearch","subSearch","authSearch","sysprefSearch","fillCallslip","courseReserves","activeTabId","unhideMasthead","toState","tabs","iface","openEditModal","notesArr","note","itemCheckinNotesQ","itemFacetConstraints","$animate","availabilityFlags","damaged","wthdrawn","notforloan","itemlost","onloan","on_hold","on_holdshelf","in_transit","is_recalled","shelving","capitalize","borrowers_details","owner","sortedItems","columns","tableHeight","tableEl","pageElHt","setVisibleItems","ts","updateColumnVis","parentObj","indexOf","item_checkin_notes","tableCtrl","domainMatch","animation","checkin_note","cnote","$nextBarcode","bcObj","barcode","doing","updateField","modLink","mods","dcq","dc","marcxml","is_patron","check_digit","sum","product","remainder","cbarvalid","duplicates","fixed","viewValue","otherNgModelCtrl","states","isFirst","checkPosition","minimum","scrollPos","top","minOff","setTimeout","body","modal","wrap","cssroot","bottomMargin","headerElem","footerElem","headerHeight","topbarElem","excludeElements","excludeHeight","totalExclude","fixLimit","absolute","shift","$target","scrollTop","getWindowScrollTop","elm","onScroll","compile","fn","modalForm","saveText","row","parentScope","replaceWith","applyFunc","matrix","columnHeader","array","checkin_fail","short","not_found","not_issued","withdrawn","long","was_lost","hold_requeued","unhandled_hold","unhandled_lost","was_on_order","unprocessed_callslip","cancelled_callslip","item_status","transfer_received","local_use","match","chain","filterArgs","mkRenderer","parsedTmpl","state","textNode","matchedChar","escapedKey","childnodes","deca","odTitle","ebook","video","odData","prodUUID","click","previewBtn","titleList","showLessNum","showMoreNum","novelistContent","kwNovelistRelatedContentLink","ui","log_url","httpReq","iframewin","lexileRanges","endGrade","startGrade","Logger","kwMaybeLink","preferred","elsewhere","showList","getItemAt","locations","cn","align","window_size","cloudBib","communityContentSvc","oldVal","isAre","pattern","lhsLength","weekdaySelected","days","halves","home","widgets","contentBlock","priority","terminal","shelfItem","noWrap","maxTitles","searchSort","shelfId","crsl","makeSlides","slide","slides","spellcheck","initialItem","half_slide_length","uri_to_id","wing_promises","half_first_shelf","shelfitems","acc","last_title","firstSlide","itm","slideContents","myIndex","myindex","triggerImg","isEdge","activeIndex","naivePluralize","cntEl","textEl","asText","ucfirst","periodicals","expanded","branchOrder","fetchedEditions","periodicalsRsrc","serialEditions","periodical","sub","branchSubs","statusOrder","stats","fields","marcField","subs","prefix","onReady","pre","$cacheFactory","applyFrom","otherValue","marcs","assertions","checkValidity","stripped","dsl","withThis","xType","xValue","yType","yValue","yStripped","xStripped","withField","withSubfield","withSubfields","skipAt","parseMarc","queue","enqueue","resendAll","request","errorCB","rejectAll","reason","echo","loginRequired","sessionExpired","kohaHttpResendQueue","events","responseError","autoReauthenticate","needsAuth","permissions","widget","registerState","matchRoute","stateParameters","paths","args","parseUrlArgs","matches","stateDef","pageSubTitle","appDef","base","checkPermissions","attrsStr","makeIframe","outerClass","widgetDef","registerRouteMatcher","searchFilter","templateProvider","grouped","staffRedirectService","doStateChange","queryParams","paramMap","tickets","ticketCount","debug","mask","ticketId","referer","postRoute","History","currentScope","previousElem","currentElem","changeCounter","cleanupLastIncludeContent","$templateRequest","widgetExp","thisChangeId","fragment","nodes","tmp","textContent","jqLiteBuildFragment","futureParentElement","itemMatches","staffLegacyAppProvider","appState","addToSidebar","legacyUrl","displayIf","circ_policy","transfer_limit","circ_alert","fine_rule","netsvc","admin","reports","dynamic","addTitle","member","merge","reviewid","list_id","mvr","io_factory","batches","import_batch_id","edit_notices","fieldMap","awLogService","trace","substitute_user","roles","branches","tmpl_id","prof_id","layout_id","addToDock","reloadOnSearch","titleClass","acquisition","addToSIdebar","widgetIdx","widgetList","resolvedPermissions","abstract","loadMarced","serie","views","marcedNav","main","editor","recordid","requiresAuth","redirectTo","ledgerPriceModels","ledger","circScopes","circControl","policy","termset","termDates","ncipAgency","subscriptions","subscription","serialInstances","serialEdition","templates","itemLists","itemList","itemListEntries","importScripts","importScriptlets","importScript","script_type","scriptlets","importScriptRuns","report_tags","report","runs","run","fullrun","cronScripts","cronJobs","cronJob","cron_script_id","purge_after","save_stderr","save_stdout","cronJobRuns","traceOptions","traces","member_id","rulesets","ruleset","marcImportItem","isUsed","staff_home_widgets","connectWith","postMessage","staffLegacyApp","patronRegSvc","sortKeys","sortedObj","batchSVC","batch","berts","importBatchService","callnumber","filterResults","begin","maxDate","test_view","bcel","exemptfine","dropbox","bib_summary","sideband","alert_tone","checkinScope","keyboard","lostItem","checkouts","libraryName","patronCount","issued","complete","clearCirc","unwatch","seenPatron","escape","showPager","filterTags","when","scheduled","tagID","displayList","hasTag","clearSelection","filter_tags","isNew","origScope","col","row_sort","col_sort","lst","rid","type_name","enableSorting","enableColumnResizing","enableFiltering","minRowsToShow","showGridFooter","enableFullRowSelection","rowTemplate","columnDefs","gridSelection","marginsConfig","docDefinition","pdfMake","defaultTmpl","node","ifaceDef","sourceTmpl","doit","labels","nodeType","section","className","childNodes","fieldSet","tagSpec","postProcess","target","trigger","itemCount","showText","staffResult","isPublic","wrapped","wrapping","reduceRight","$marcDisplay","insertTarget","visibility","where","ItemFields","edited","toDelete","deleteCount","retry","retries","moved","itemsToMove","h","mfhdid","afterEdits","openEditor","seen","holdingsP","oneTime","updateIssues","stash","renewals","lastIssue","updateLostItem","stashStatuses","maybeWarn","lost_item","lost_handled","persist","callModal","itemlvl","statusid","addMulti","dupe","optionsDrop","cloneItem","newItem","newItemDefaults","bib_cn","userHiddenFields","doNotClose","editorRV","requestedBarcode","saveError","extraItemCount","errors","userRoles","branchParameter","userParameter","checkValid","load","searchValue","loadThrottle","deets","generic","makeTicket","messageBox","ids","price_model_parameters","basis","breakpoint_type","volume_discount_overrides_base","admin_percent","branch_code","table","ctable","sels","vendor_ids","update_action","level","price_model","is_default","vendor_code","access_status","pricing_status","product_subtype","ldg","doSave","ledger_ids","criteria","scripts","kwImportFactorySvc","OTAC","SUB","UPAK","reindex","modelDefs","pdesc","defs","ivCount","dvCount","ledgerCopy","byValue","ptype","importScriptlet","pipelineType","pipelineTypes","sheet","bibselect","bibfinalize","display_name","pt","scriptlet","availableComponents","enableGridMenu","exporterPdfDefaultStyle","fontSize","exporterPdfHeader","exporterPdfFooter","exporterPdfCustomFormatter","gridApi","userPrefs","yourPrefs","skipped","targetState","shadowQuery","noResults","$id","limitQuery","orderBy","responses","ACQLIST","CATALOG","CATALOGUING","AUTHORITIES","CIRCULATION","MEMBERS","SOLRSYNC","SERIAL","SYSTEMPREFERENCE","since","start_date","end_date","y1","dummy","curr_date","curr_month","date_string","moduleName","transformObject","flatEntityAccess","enablePagination","enableColumnMenus","enableRowSelection","enableRowHeaderSelection","enableFooterTotalSelected","multiSelect","sortable","displayName","fm","age","debit","invoicetypes","can_invoice","accttype","accruing_balance","balance","net_credit","pmt","selection","selectAll","pmtsShown","isPmtLinked","paymentsPromise","pmts","updateView","updateSummary","loadAllTxns","PAYMENT","credit_action","blocking","backgroundClick","txns","amount","credit","to_credit","affectedFees","syncFees","updateAffected","feeTypes","class_desc","parseFloat","payment","refunds","acctLabel","verb","REVERSED_PAYMENT","CANCELCREDIT","REFUND","txn","brief","itemnumber","duedate","lookup","nextInput","evt","getPatron","renderMembers","createBorrowers","flat_bfields","source_patron_id","pid","surname","firstname","dateofbirth","initials","othernames","sex","guarantorid","contactname","contactfirstname","relationship","address","address2","city","zipcode","country","phone","phonepro","mobile","smsalertnumber","emailpro","fax","B_address","B_address2","B_city","B_zipcode","B_country","B_phone","B_email","contactnote","altcontactsurname","altcontactfirstname","altcontactaddress1","altcontactaddress2","altcontactaddress3","altcontactzipcode","altcontactcountry","altcontactphone","categorycode","sort1","sort2","dateenrolled","dateexpiry","opacnote","borrowernotes","userid","xattr","force","lostid","outstanding","settled","sdate","creditByDate","fee_summary","creditByType","inFull","compose_fee_summary","lostitems","_display","li","updateLinkedFees","undo","serials","branchCode","_embed_total_rows","inner_mod","mod","linked","inner_count","chronology","sequence","predictions","hit","infer","patterns","sequence_config","chronology_state","sequence_state","pattern_index","pattern_description","chronology_template","sequence_template","sequence_increment","parentReceiptPattern","serial_pattern_templates","serial_chronology_templates","serial_sequence_templates","periodical_id","supplement","branch_name","va","vendor_account_id","custom","newField","byId","instances","bDate","aDate","bvPrintSvc","disableWatch","bib_id","enableWatch","bestAddr","fallbackEmail","bestEmail","altEmail","fallbackAddr","altAddr","claimed","print","edate","daysDiff","newStart","newEnd","editing","_is_basic","vendor_accounts","vendorIdx","vendor_account","countries","addresses","verbose","entries","keyField","fixedKeyField","skipInvalid","operation","selectedItems","itemStatuses","enableColumnReordering","enableSelectAll","saveWidths","saveOrder","saveScroll","saveFocus","saveVisible","saveSort","saveFilter","savePagination","savePinning","saveGrouping","saveGroupingExpandedStates","saveTreeView","saveSelection","cellTemplate","selectOptions","$interval","item_list_id","itemFieldIndex","_action","_itemstatus","mod_item","delete_title","selected_items","hasBranch","filteredBranches","semiFlat","totalCategories","mt","availableTemplates","templateCodeBranches","branchTemplates","templateCode","templateSet","canEdit","owningBranch","isPrimary","flatTemplates","allFlatTemplates","category","cloned","applyToBranch","allTemplates","messageType","endpointTypes","typeDef","templateCodes","selections","tt","enabledTraceOptions","forUser","duration","save","groupPolicies","groups","baseArgs","ccIdx","specified","precedences","unlimitedIfNull","variable","scopeOptions","defaultScope","scopeClass","defaultUnit","unitOptions","unitClass","integer","ifFunc","enabled","newPolicy","hdr","rule","permit","policies","termsets","branch_group","item_type","patron_category_code","cascaded","newRule","termDate","circ_termset_id","limit","blocks","block","sanitizeInput","recipientType","endpoint","immediate","users","new_user_id","shutdown","reqParams","defaultValue","buildGridControlScope","actionFunction","lengthConstraint","valueConstraint","original","ctrlMultiSelect","selectCheckbox","enablePaginationControls","paginationPageSizes","paginationPageSize","modifierKeysToMultiSelect","style","onRegisterApi","cellFilter","width","doAdd","acqListsModel","acqListControlsAPI","datum","listitems","needsCheck","renewLists","mergeLists","openList","updateSubscriptions","sel","updateRows","newHoldings","bad_dates","missing","newName","default_id","radioModel","entryListItem","hashVals","hasRenew","calc","renewed","start_year","end_year","renewal_month","oneRenewable","oneBadName","api","oldStatus","selectors","candidate","shared","list_accessors","formfields","workform","download","return_url","acqListsCartSvc","limitedTo","totalHits","typeMap","listFound","currentAcqListId","program","order_type","searchDeferred","program_values","order_type_values","noneAt","acqListUtilAPI","discounts","accessors","branchOrders","appendSearchResults","shareWith","getAllEntries","deleteAllEntries","updateAllEntries","mergeAll","renewAll","remindAll","setStatusAll","export","checkHoldings","checkHoldingsFor","commit","addToPortalCart","goToPortalCart","getPortalCart","getPortalRoutes"],"sourceRoot":"webpack:///","sources":["app/js/model.js","app/js/services.js","app/js/lucene-peg-parser.js","app/js/lucene-query-parser.js","app/js/controllers.js","app/js/filters.js","app/js/preload-templates.js","app/js/directives.js","app/lib/koha_sessioninterceptor.js","app/js/staff-services.js","app/js/staff-apps.js","app/js/staff.js","app/js/ui-grid-glue.js","app/js/acqlists.js"],"sourcesContent":["/* global X2JS */\n(function(){\n\n'use strict';\n\n/* Model */\n\nvar module = angular.module('kohapac.model', []);\n\nmodule.factory('kwApi', [\"$resource\", \"$http\", \"$cacheFactory\", \"configService\", \"bvItemSvc\", \"bvMfhdSvc\", function($resource, $http, $cacheFactory, configService, bvItemSvc, bvMfhdSvc ){\n\n function transformApiQuery (mapping) {\n return function(jsondata) {\n try {\n var raw = JSON.parse(jsondata);\n var items = raw.map(function(item) {\n if (mapping) item = mapping(item);\n return item;\n });\n //console.dir(items);\n return items;\n }\n catch (e) {\n console.warn(e);\n return jsondata;\n }\n };\n }\n\n function transformApiGet (mapping) {\n return function(jsondata) {\n try {\n var item = JSON.parse(jsondata);\n if (mapping) item = mapping(item);\n return item;\n }\n catch (e) {\n return jsondata;\n }\n };\n }\n\n function expandMarc (srcKey, targetKey) {\n return function(jsonOrObj) {\n var rsp = (angular.isString(jsonOrObj)) ? angular.fromJson(jsonOrObj) : jsonOrObj;\n if(!srcKey) srcKey = 'marc';\n if(rsp[srcKey])\n rsp[targetKey || srcKey] = new MarcRecord(rsp[srcKey]);\n return rsp;\n };\n }\n\n var addTransform = function(which, transform) {\n var defaults = $http.defaults[which];\n defaults = angular.isArray(defaults) ? defaults : [defaults];\n return (which=='transformRequest') ? [transform].concat(defaults) : defaults.concat(transform);\n };\n var itemfieldDefs = {};\n configService.ItemFields.forEach(function(df){ itemfieldDefs[df.code] = df; });\n\n\n var svc = {\n Work : $resource('/api/work/:id',{}, {\n get: {\n method: 'GET',\n transformResponse: addTransform('transformResponse', expandMarc() )\n }\n }),\n Mfhd : $resource('/api/mfhd/:id', { id: \"@id\" }, {\n workMfhds: {\n method: 'GET',\n url: '/api/work/:id/mfhds',\n isArray: true,\n // XXX TODO move this\n transformResponse: function(jsondata){\n var data = angular.fromJson(jsondata);\n return data.map(function(mfhdAndUri){\n mfhdAndUri.mfhd.marc_record = new MarcRecord(mfhdAndUri.mfhd.marc);\n return bvMfhdSvc.make(mfhdAndUri.mfhd);\n });\n },\n }\n }),\n Prefilter: $resource('/api/opac/:query/facets', { query: \"*:*\" },\n { get : { method : 'GET',\n cache : $cacheFactory('mhprefilter', { number : 100 }),\n isArray : true\n } } ),\n\n Item : $resource('/api/item/:id', { id: \"@itemnumber\" } , {\n get : {\n method: 'GET',\n transformResponse: transformApiGet(\n function(e) { return bvItemSvc.make(e); }),\n },\n getCnBrowse : {\n method: 'GET',\n url: '/api/item',\n params: {\n view: 'cnb',\n branch: null,\n ccode: null\n },\n isArray: true\n },\n workItems : {\n method: 'GET',\n url: '/api/work/:id/items',\n isArray: true,\n transformResponse: transformApiQuery(\n function(e) { return bvItemSvc.make(e.item);}),\n },\n getByBarcode : {\n method: 'GET',\n transformResponse: transformApiGet(\n function(e) { return bvItemSvc.make(e._embed.item);}),\n },\n nextBarcode : {\n method: 'POST',\n url: '/api/item',\n params: {\n op: 'available_barcode',\n branch: null\n }\n },\n relinkMfhd : { // returns 204\n method: 'POST',\n params: { op: 'relink_mfhd',\n target_mfhd_id: null\n }\n },\n create : {\n method: 'POST',\n transformRequest: addTransform('transformRequest', bvItemSvc.transformApiSave ),\n params: {\n op: 'create'\n },\n transformResponse: transformApiGet(\n function(e) { return bvItemSvc.make(e); })\n },\n put : {\n method: 'PUT',\n transformRequest: addTransform('transformRequest', bvItemSvc.transformApiSave ),\n transformResponse: transformApiGet(\n function(e) { return bvItemSvc.make(e); })\n },\n delete : {\n method: 'DELETE'\n },\n cancelTransfer : {\n method: 'POST',\n params: {\n op: 'cancel_transfer'\n },\n transformResponse: transformApiGet(\n function(e) { return bvItemSvc.make(e); })\n },\n checkin : {\n method: 'POST',\n params: {\n op: 'checkin'\n },\n transformResponse: addTransform('transformResponse',\n function( data, h, status ){\n // FIXME: we get lost item data on conflict rather than item.\n return (status=='409' || status=='403' || status=='404') ? data : bvItemSvc.make(data);\n })\n },\n setStatus : {\n method: 'POST',\n params: {\n op: 'set_status'\n },\n transformResponse: transformApiGet(\n function(e) { return bvItemSvc.make(e); })\n },\n clearStatus : {\n method: 'POST',\n params: {\n op: 'clear_status'\n },\n transformResponse: transformApiGet(\n function(e) { return bvItemSvc.make(e); })\n },\n offlineCheckout : {\n method: 'POST',\n params: {\n op: 'offline_checkout'\n },\n // transformResponse: function(jsondata){\n // try{\n // var data = angular.fromJson(jsondata);\n // return xformItemLoad(data._embed.item);\n // } catch (e) {\n // return jsondata;\n // }\n // }\n },\n testRecall: {\n method: 'POST',\n params: {\n op: 'test_recall'\n }\n },\n recall: {\n method: 'POST',\n params: {\n op: 'recall'\n }\n }\n }),\n Dlso: $resource('/api/dlso/:id', {}, {\n get: {\n method: 'GET'\n },\n metadata: {\n method: 'GET'\n },\n summary: {\n url: '/api/dlso/:id/summary',\n method: 'GET',\n },\n bib: {\n url: '/api/dlso/:id/linked-work',\n method: 'GET'\n }\n }),\n Hold : $resource('/api/hold/:id', { id: '@reservenumber' }, {\n\n confirm_fill: {\n method: 'POST',\n params: {\n op: 'confirm_fill'\n }\n },\n print_slip: {\n method: 'GET',\n params: {\n op: 'print_slip',\n send_to: null\n }\n },\n getForPatron: {\n method: 'GET',\n isArray: true,\n url: '/api/patron/:id/holds',\n transformResponse: function(jsondata){\n var data = angular.fromJson(jsondata);\n return data.map(function(holdAndUri){\n return holdAndUri.hold;\n });\n },\n },\n testPlace: {\n method: 'POST',\n url: '/api/work/:bibid/holds',\n params: {\n op: 'check_place'\n }\n },\n create: {\n method: 'POST',\n url: '/api/work/:bibid/holds',\n params: {\n op: 'place'\n }\n },\n cancel: {\n method: 'DELETE',\n },\n requeue: {\n method: 'POST',\n params: {\n op: 'requeue'\n }\n },\n resume: {\n method: 'POST',\n params: {\n op: 'resume'\n }\n },\n suspend: {\n method: 'POST',\n params: {\n op: 'suspend'\n }\n }\n\n }),\n ItemCheckinNote : $resource('/api/item/:item_id/checkin-note', { item_id: '@item_id' }, {\n update: {\n method: 'PUT'\n },\n getForBib: {\n method: 'GET',\n url: '/api/work/:bibid/item-checkin-notes',\n isArray: true\n }\n }),\n ItemStatus : $resource('/api/itemstatus/:id', { }, {\n getAll: {\n method: 'GET',\n isArray: true\n },\n update: {\n method: 'PUT'\n }\n }),\n Issue : $resource('/api/issue/:id', { id: '@id' }, {\n get : {\n method: 'GET',\n transformResponse: transformApiGet(\n function(e) { e.itemSummary = e._embed.item_summary; return e; }),\n },\n itemIssues : {\n method: 'GET',\n isArray: true,\n url: '/api/item/:id/issues',\n transformResponse: transformApiQuery( function(issueAndUri){\n var issue = issueAndUri.issue;\n issue.itemSummary = issue._embed.item_summary;\n return issue;\n })\n },\n getForPatron: {\n method: 'GET',\n isArray: true,\n url: '/api/patron/:id/issues',\n transformResponse: transformApiQuery( function(issueAndUri){\n var issue = issueAndUri.issue;\n issue.itemSummary = issue._embed.item_summary;\n return issue;\n })\n },\n testRenew: {\n method: 'POST',\n params: {\n op: 'test_renew' // non-mutating, so no transformResponse.\n }\n },\n renew: {\n method: 'POST',\n params: {\n op: 'renew'\n },\n transformResponse: transformApiGet( function(issue){\n issue.itemSummary = issue._embed.item_summary;\n\n // console.log(angular.copy(issue));\n return issue;\n })\n }\n }),\n LostItem : $resource('/api/lost-item/:id', { id: '@id' }, {\n get: {\n method: 'GET'\n },\n checkin: {\n method: 'POST',\n params: {\n op: 'checkin',\n refund: 'LOSTRETURNED',\n remove: true\n }\n },\n defer: {\n method: 'POST',\n params: {\n op: 'defer'\n }\n },\n claimReturned: {\n method: 'POST',\n params: {\n op: 'claimreturned'\n }\n },\n getforPatron: {\n method: 'GET',\n isArray: true,\n url: '/api/patron/:id/lost-items',\n params: {\n // withfees: true\n },\n transformResponse: function(jsondata){ // FIXME: Add type data so we don't have to do this.\n var data = angular.fromJson(jsondata);\n return data.map(function(lostitem){\n ['biblionumber','id','issue_id','itemnumber'].forEach(function(key){\n if(lostitem[key]) lostitem[key] = parseInt(lostitem[key]);\n });\n return lostitem;\n });\n },\n }\n }),\n\n AccountType : $resource('/api/accounttype/:accounttype', { accounttype: '@accounttype'}, {\n getList: {\n method: 'GET',\n isArray: true,\n },\n put: {\n method: 'PUT',\n },\n }),\n PatronAccount : $resource('/api/patron/:id/account-summary', { id: '@id' }, {\n sendAlert: {\n method: 'POST',\n params: {\n op: 'send_alert',\n }\n },\n emailReceipt: {\n method: 'POST',\n params: {\n op: 'email_receipt',\n }\n },\n getReceipt: {\n method: 'POST',\n params: {\n op: 'get_receipt',\n }\n },\n redistributeCredits: {\n method: 'POST',\n params: {\n op: 'redistribute_credits',\n }\n },\n }),\n\n WorkAttributes : $resource('/api/work/:id/attributes', {}, {\n get: {\n method: 'GET',\n isArray: true\n }\n }),\n Branch: $resource('/api/branch/:id', {}, {\n getList: {\n method: 'GET',\n url: '/api/branch?view=picker',\n isArray: true,\n },\n getVendorList: {\n method: 'GET',\n url: '/api/branch?view=picker&scope=vendor',\n isArray: true,\n },\n getAcqVendors: {\n method: 'GET',\n url: '/api/branch/:id/acq-vendors',\n isArray: true,\n },\n getAcqVendorAccounts: {\n method: 'GET',\n url: '/api/branch/:id/acq-vendor-accounts',\n isArray: true,\n },\n getAcqVendorCountries: {\n method: 'GET',\n url: '/api/branch/:id/acq-vendor-accounts?view=countries',\n isArray: true,\n },\n deleteAcqVendorAccount: {\n method: 'POST',\n url: '/api/branch/:id/acq-vendor-accounts/?op=delete',\n },\n saveAcqVendorAccount: {\n method: 'POST',\n url: '/api/branch/:id/acq-vendor-accounts',\n },\n getAcqPurchaseOrders: {\n method: 'GET',\n url: '/api/branch/:id/acq-purchase-orders',\n isArray: true,\n },\n getAcqPurchaseOrderLines: {\n method: 'GET',\n url: '/api/branch/:id/acq-purchase-order-lines',\n isArray: true,\n },\n getAcqSubscriptionPOLines: {\n method: 'GET',\n url: '/api/branch/:id/acq-subscription-po-lines',\n isArray: true,\n },\n pairAcqSubscriptionPOLine: {\n method: 'POST',\n url: '/api/branch/:id/acq-subscription-po-lines/?op=pair',\n },\n getSerialInstances: {\n method: 'GET',\n url: '/api/branch/:id/serial-instances',\n isArray: true,\n },\n getSubscriptions: {\n method: 'GET',\n url: '/api/branch/:id/subscriptions',\n isArray: true,\n },\n getNcipAgencies: {\n method: 'GET',\n url: '/api/branch/:id/ncip-agencies',\n isArray: true,\n },\n }),\n Vendor: $resource('/api/vendor/:id', {}, {\n getList: {\n method: 'GET',\n url: '/api/vendor?scope=all',\n isArray: true,\n },\n getLedgers: {\n method: 'GET',\n url: '/api/vendor/:id/ledgers',\n isArray: true,\n },\n getLedger: {\n method: 'GET',\n url: '/api/vendor/:id/ledger/:ledger_id',\n },\n deprecateAll: {\n method: 'POST',\n url: '/api/vendor/:id?op=deprecate_all'\n },\n purgeAll: {\n method: 'POST',\n url: '/api/vendor/:id?op=purge_all'\n },\n put: {\n method: 'PUT',\n },\n getClassificationTable: {\n method: 'GET',\n url: '/api/vendor/:id/classifications',\n isArray: true,\n },\n setClassificationTable: {\n method: 'POST',\n url: '/api/vendor/:id/classifications',\n isArray: true,\n }\n }),\n\n Ledger: $resource('/api/ledger/:id', {}, {\n getPriceModelConfig: {\n method: 'GET',\n url: '/api/ledger/?view=price-model-config',\n isArray: true,\n },\n deprecateAll: {\n method: 'POST',\n url: '/api/ledger/:id?op=deprecate_all'\n },\n purgeAll: {\n method: 'POST',\n url: '/api/ledger/:id?op=purge_all'\n },\n put: {\n method: 'PUT',\n },\n }),\n\n PatronGroup: $resource('/api/patron/:id/group/:mid', { id: '@id', mid: '@mid' }, {\n getGroup: {\n method: 'GET',\n url: '/api/patron/:id/group',\n isArray: true,\n },\n rmGroupMember: {\n method: 'DELETE',\n url: '/api/patron/:id/group/:mid',\n },\n addGroupMember: {\n method: 'POST',\n url: '/api/patron/:id/group/:mid',\n params: {rel: '@rel', op: 'add-group-member', force: '@force'},\n },\n updateRelationship: {\n method: 'POST',\n url: '/api/patron/:id/group/:mid',\n params: {rel: '@rel', op: 'update-group-member'},\n },\n }),\n PatronRegistration: $resource('/api/patron-registration/:id', { id: '@id' }, {\n getApps: {\n method: 'GET',\n url: '/api/patron-registration/',\n isArray: true,\n },\n rejectApp: {\n method: 'POST',\n url: '/api/patron-registration/:id',\n params: { op: 'update-app' }\n },\n approveApp: {\n method: 'POST',\n url: '/api/patron-registration/:id',\n params: { op: 'update-app' }\n },\n submitApp: {\n method: 'POST',\n url: '/api/patron-registration/',\n params: { op: 'register-patron' }\n },\n }),\n Patron: $resource('/api/patron/:id', { id: '@id' }, {\n getBranchList: {\n method: 'GET',\n url: '/api/patron/?view=picker&scope=branch&permit=:permit',\n isArray: true,\n },\n getSuEligibleList: {\n method: 'GET',\n url: '/api/patron/?view=picker-extended&category_code=nontemp&subpermissions=1',\n isArray: true,\n },\n getRoleList: {\n method: 'GET',\n url: '/api/patron/?view=picker-extended&category_code=ROLE',\n isArray: true,\n },\n generateSuTicket: {\n method: 'POST',\n url: '/api/patron/:id?op=generate-su-ticket',\n },\n makeTemplatePatron: {\n method: 'POST',\n url: '/api/patron/?op=make-template-patron',\n },\n getSocialMediaAccounts: {\n method: 'GET',\n url: '/api/patron/:id/social-media-accounts',\n isArray: true,\n },\n mergePatron: {\n method: 'POST',\n url: '/api/patron/:id',\n params: { op: 'merge', source_patron_id: '@source_patron_id', target_patron_id: '@target_patron_id', bfields: '@bfields' }\n },\n deletePatron: {\n method: 'DELETE',\n url: '/api/patron/:id',\n params: { test: '@test' }\n },\n }),\n Catalog: $resource('/api/opac/:query', {}, {\n query: {\n method: 'GET',\n url: '/api/opac/:query',\n isArray: false,\n },\n exportSearch: {\n method: 'POST',\n url: '/api/opac/?op=export-search'\n },\n }),\n ImportScript: $resource('/api/import-script/:id', {}, {\n getList: {\n method: 'GET',\n url: '/api/import-script',\n isArray: true,\n },\n execute: {\n method: 'POST',\n url: '/api/import-script/:id?op=execute'\n },\n put: {\n method: 'PUT',\n },\n deleteAll: {\n method: 'POST',\n url: '/api/import-script?op=delete-all'\n },\n deepCopy: {\n method: 'POST',\n url: '/api/import-script/:id?op=deep-copy'\n },\n getParameters: {\n method: 'GET',\n url: '/api/import-script/:id/parameter-sets',\n isArray: true,\n },\n getRuns: {\n method: 'GET',\n url: '/api/import-script/:id/runs',\n isArray: true,\n },\n }),\n ImportScriptParameterSet: $resource('/api/import-script-parameter-set/:id', {}, {\n put: {\n method: 'PUT',\n },\n }),\n ImportScriptlet: $resource('/api/import-scriptlet/:id', {}, {\n getList: {\n method: 'GET',\n url: '/api/import-scriptlet',\n isArray: true,\n },\n put: {\n method: 'PUT',\n },\n deleteAll: {\n method: 'POST',\n url: '/api/import-scriptlet?op=delete-all'\n },\n approveAll: {\n method: 'POST',\n url: '/api/import-scriptlet?op=approve-all'\n },\n }),\n ImportScriptRun: $resource('/api/import-script-run/:id', {}, {\n forAll: {\n method: 'POST',\n url: '/api/import-script-run?op=forall',\n },\n }),\n ActionLog: $resource('/api/action-log/:id', {}, {\n getFieldMap: {\n method: 'GET',\n url: '/api/action-log?view=field_map',\n isArray: true\n },\n getList: {\n method: 'GET',\n url: '/api/action-log',\n isArray: true\n },\n getStats: {\n method: 'GET',\n url: '/api/action-log?view=stats'\n },\n purge: {\n method: 'POST',\n url: '/api/action-log?op=purge'\n },\n }),\n Login: $resource('/api/login', {}, {\n linkSocialInit: {\n method: 'POST',\n url: '/api/login?op=social-bind',\n },\n linkSocialFinalize: {\n method: 'POST',\n url: '/api/login?op=social-bind-finalize',\n },\n loginSocialInit: {\n method: 'POST',\n url: '/api/login?op=social-login',\n },\n loginSocialFinalize: {\n method: 'POST',\n url: '/api/login?op=social-login-finalize',\n },\n lostPass: {\n method: 'POST',\n url: '/api/lostpass',\n },\n\n }),\n // Serials\n Periodical: $resource('/api/periodical/:id', {}, {\n search: {\n method: 'GET',\n url: '/api/periodical/?view=search',\n isArray: true,\n },\n getForBib: {\n method: 'GET',\n url: '/api/work/:bibid/periodicals',\n isArray: true,\n },\n getSubscriptions: {\n method: 'GET',\n url: '/api/periodical/:id/subscriptions',\n isArray: true,\n },\n getSerialEditions: {\n method: 'GET',\n url: '/api/periodical/:id/serial-editions',\n isArray: true,\n },\n put: {\n method: 'PUT',\n },\n // Non-mutating, but potentially large args\n predict: {\n method: 'POST',\n url: '/api/periodical?op=predict',\n isArray: true,\n },\n generateEdition: {\n method: 'POST',\n url: '/api/periodical/:id?op=generate',\n },\n deleteWithItem: {\n method: 'POST',\n url: '/api/periodical/:id?op=delete-with-item',\n },\n }),\n Subscription: $resource('/api/subscription/:id', {}, {\n getSerialInstances: {\n method: 'GET',\n url: '/api/subscription/:id/serial-instances',\n isArray: true,\n },\n get: {\n method: 'GET',\n transformResponse: addTransform('transformResponse', expandMarc('holdings') )\n },\n put: {\n method: 'PUT',\n },\n deleteWithItem: {\n method: 'POST',\n url: '/api/subscription/:id?op=delete-with-item',\n },\n }),\n SerialEdition: $resource('/api/serial-edition/:id', {}, {\n getSerialInstances: {\n method: 'GET',\n url: '/api/serial-edition/:id/serial-instances',\n isArray: true,\n },\n put: {\n method: 'PUT',\n },\n deleteWithItem: {\n method: 'POST',\n url: '/api/serial-edition/:id?op=delete-with-item',\n },\n }),\n SerialInstance: $resource('/api/serial-instance/:id', {}, {\n saveAll: {\n method: 'POST',\n url: '/api/serial-instance/?op=save-all',\n isArray: true,\n },\n claim: {\n method: 'POST',\n url: '/api/serial-instance/?op=claim',\n },\n deleteWithItem: {\n method: 'POST',\n url: '/api/serial-instance/:id?op=delete-with-item',\n },\n }),\n SerialPatternTemplate: $resource('/api/serial-pattern-template/:id', {}, {\n put: {\n method: 'PUT',\n },\n }),\n SerialChronologyTemplate: $resource('/api/serial-chronology-template/:id', {}, {\n put: {\n method: 'PUT',\n },\n }),\n SerialSequenceTemplate: $resource('/api/serial-sequence-template/:id', {}, {\n put: {\n method: 'PUT',\n },\n }),\n ItemTemplate: $resource('/api/item-defaults/:id', { id: \"@id\" }, {\n getList: {\n method: 'GET',\n isArray: true\n },\n create: {\n method: 'POST',\n params: { op: 'create' }\n }\n }),\n WorksList: $resource('/api/works-list/:id', {}, {\n put: {\n method: 'PUT',\n },\n }),\n\n ItemList: $resource('/api/item-list/:id', {}, {\n put: {\n method: 'PUT',\n },\n getEntries: {\n method: 'GET',\n url: '/api/item-list/:id/entries',\n isArray: true,\n },\n saveWithEntries: {\n method: 'POST',\n url: '/api/item-list/:id?op=save-with-entries',\n },\n saveFromSearch: {\n method: 'POST',\n url: '/api/item-list/:id?op=save-from-search',\n },\n saveFromUpload: {\n method: 'POST',\n url: '/api/item-list/:id?op=save-from-upload',\n },\n validateEntries: {\n method: 'POST',\n url: '/api/item-list/:id?op=validate-entries',\n isArray: true,\n },\n saveMetadata: {\n method: 'POST',\n url: '/api/item-list/:id?op=save-metadata',\n },\n setName: {\n method: 'POST',\n url: '/api/item-list/:id?op=set-name',\n },\n modifyItems: {\n method: 'POST',\n url: '/api/item-list/:id?op=modify-items',\n },\n deleteItems: {\n method: 'POST',\n url: '/api/item-list/:id?op=delete-items',\n },\n\n }),\n ItemListEntry: $resource('/api/item-list-entry/:id', {}, {\n forAll: {\n method: 'POST',\n url: '/api/item-list-entry?op=forall',\n },\n }),\n Message: $resource('/api/message/:id', {}, {\n put: {\n method: 'PUT',\n },\n send: {\n method: 'POST',\n url: '/api/message?op=send',\n },\n }),\n MessageTemplate: $resource('/api/message-template/:id', {}, {\n tree: {\n method: 'GET',\n url: '/api/message-template?view=tree',\n isArray: true,\n },\n getTemplateCodes: {\n method: 'GET',\n url: '/api/message-template?view=template-codes',\n isArray: true,\n },\n put: {\n method: 'PUT',\n },\n forAll: {\n method: 'POST',\n url: '/api/message-template?op=forall',\n },\n applyTo: {\n method: 'POST',\n url: '/api/message-template?op=apply-to',\n },\n }),\n MessageType: $resource('/api/message-type/:id', {}, {\n getWithParameters: {\n method: 'GET',\n url: '/api/message-type/:id?view=with-parameters',\n },\n setBranchTemplate: {\n method: 'POST',\n url: '/api/message-type/:id?op=set-branch-template',\n },\n replaceTemplateSet: {\n method: 'POST',\n url: '/api/message-type/:id?op=replace-template-set',\n },\n }),\n Fee: $resource('/api/fee/:id', { id: '@id'}, {\n get: {\n method: 'GET'\n },\n getFromLostItem: {\n method: 'GET',\n url: '/api/lost-item/:lostid/fees',\n isArray: true\n },\n waive: {\n method: 'POST',\n hasBody: false,\n params: {\n op: 'waive'\n }\n },\n getForPatron: {\n method: 'GET',\n isArray: true,\n url: '/api/patron/:patron_id/fees/:type',\n // FIXME: fees/unallocated returns a non-fee .\n },\n create: {\n method: 'POST',\n params: { op: 'create' },\n }\n }),\n Payment: $resource('/api/payment/:id', { id: '@id' }, {\n get: {\n method: 'GET',\n },\n getForPatron: {\n method: 'GET',\n isArray: true,\n url: '/api/patron/:patron_id/payments',\n },\n create: {\n method: 'POST',\n params: { op: 'create' },\n },\n setReallocate: {\n method: 'POST',\n params: { op: 'set_reallocate' },\n },\n reverse: {\n method: 'POST',\n params: { op: 'reverse' },\n },\n webPay: {\n method: 'POST', // creates payment from paypal order. // capture if needed.\n params: {\n op: 'webpay',\n gateway: 'paypal',\n // required: order_id\n }\n },\n paypalCustToken: {\n method: 'GET',\n url: '/api/payment-gateway/paypal/client_token'\n },\n createWebpayOrder: {\n method: 'POST',\n url: '/api/payment-gateway/paypal/create_order'\n }\n }),\n Report: $resource('/api/report/:id', {}, {\n getRuns: {\n method: 'GET',\n url: '/api/report/:id/runs',\n isArray: true,\n },\n getParameters: {\n method: 'GET',\n url: '/api/report/:id/parameters',\n isArray: true,\n },\n execute: {\n method: 'POST',\n url: '/api/report/:id?op=execute',\n },\n schedule: {\n method: 'POST',\n url: '/api/report/:id?op=schedule',\n },\n forAll: {\n method: 'POST',\n url: '/api/report?op=forall',\n },\n put: {\n method: 'PUT',\n },\n getRun: {\n method: 'GET',\n url: '/api/report/:id/run/:rid',\n },\n deleteRun: {\n method: 'DELETE',\n url: '/api/report/:id/run/:rid',\n },\n exportRunAsBatch: {\n method: 'POST',\n url: '/api/report/:id/run/:rid?op=export-batch',\n },\n exportRunAsItemList: {\n method: 'POST',\n url: '/api/report/:id/run/:rid?op=export-itemlist',\n },\n }),\n ReportTag: $resource('/api/report-tag', {}, {}),\n CronJob: $resource('/api/cron-job/:id', {}, {\n getRuns: {\n method: 'GET',\n url: '/api/cron-job/:id/runs',\n isArray: true,\n },\n getRun: {\n method: 'GET',\n url: '/api/cron-job/:id/run/:rid',\n },\n deleteRun: {\n method: 'DELETE',\n url: '/api/cron-job/:id/run/:rid',\n },\n put: {\n method: 'PUT',\n },\n execute: {\n method: 'POST',\n url: '/api/cron-job/:id?op=execute',\n },\n schedule: {\n method: 'POST',\n url: '/api/cron-job/:id?op=schedule',\n },\n }),\n CronScript: $resource('/api/cron-script/:id', {}, {}),\n CircControl: $resource('/api/circ-control/:id', {}, {\n put: {\n method: 'PUT',\n },\n getScopes: {\n method: 'GET',\n url: '/api/circ-control/?view=scopes',\n isArray: true,\n },\n }),\n CircPolicy: $resource('/api/circ-policy/:id', {}, {\n put: {\n method: 'PUT',\n },\n }),\n CircRule: $resource('/api/circ-rule/:id', {}, {\n put: {\n method: 'PUT',\n },\n testTuple: {\n method: 'POST',\n url: '/api/circ-rule/?op=test-tuple',\n isArray: true,\n }\n }),\n CircTermset: $resource('/api/circ-termset/:id', {}, {\n put: {\n method: 'PUT',\n },\n getDates: {\n method: 'GET',\n url: '/api/circ-termset/:id/dates',\n isArray: true,\n },\n }),\n CircTermDate: $resource('/api/circ-term-date/:id', {}, {\n put: {\n method: 'PUT',\n },\n }),\n RecallRule: $resource('/api/recall-rule/:id', {}, {\n put: {\n method: 'PUT',\n },\n testTuple: {\n method: 'POST',\n url: '/api/recall-rule/?op=test-tuple',\n isArray: true,\n }\n }),\n\n MarcValidationRuleset: $resource('/api/marc-validation-ruleset/:id', {}, {\n checkSyntax: {\n method: 'POST',\n url: '/api/marc-validation-ruleset/?op=check-syntax',\n },\n checkAllSyntax: {\n method: 'POST',\n url: '/api/marc-validation-ruleset/:id?op=check-syntax',\n isArray: true,\n },\n put: {\n method: 'PUT',\n url: '/api/marc-validation-ruleset/:id',\n },\n }),\n NcipAgency: $resource('/api/ncip-agency/:id', {}, {\n put: {\n method: 'PUT',\n url: '/api/ncip-agency/:id',\n },\n forAll: {\n method: 'POST',\n url: '/api/report?op=forall',\n },\n }),\n Worldcat: $resource('/api/worldcat', {}, {\n getOpenUrlResolve: {\n method: 'GET',\n url: '/api/worldcat/?op=openurl',\n isArray: true,\n },\n getEntriesSearch: {\n method: 'GET',\n url: '/api/worldcat/?op=entries',\n isArray: false,\n },\n getCollectionsSearch: {\n method: 'GET',\n url: '/api/worldcat/?op=collections',\n isArray: false,\n },\n }),\n SessionApiMetadata: $resource('/api/session-api-metadata/:id', {}, {\n generateError: {\n method: 'POST',\n url: '/api/session-api-metadata?op=throw',\n },\n traceUser: {\n method: 'POST',\n url: '/api/session-api-metadata?op=trace-user',\n },\n options: {\n method: 'GET',\n url: '/api/session-api-metadata?view=options',\n isArray: true,\n },\n }),\n MarcDisplayTmpl: $resource('/api/marcdisplaytmpl/:id', { id : '@id'}, {\n getAll: {\n method: 'GET',\n isArray: true\n },\n getDefaults: {\n method: 'GET',\n isArray: true,\n params: {\n op: 'get_defaults'\n }\n }\n })\n };\n\n bvItemSvc.extendModel(svc.Item);\n bvMfhdSvc.extendModel(svc.Mfhd);\n\n svc.Report.$validateSqlPattern = function(value) {\n var result = (value.length > 0) ? value.replace(/(WHERE|HAVING).*?(;|'$)/ig, \"\").match(/^(?![\\s\\S]*\\b(UPDATE|DELETE|DROP|INSERT|SHOW|CREATE|ALTER|REPLACE)\\b)/i) : true;\n if (result) {\n return true;\n }\n return false;\n }; \n svc.Report.$exportDownloadLink = function(reportId, runId, params) {\n var s = '/api/report/' + reportId + '/run/' + runId+ '?view=export&format=' + encodeURIComponent(params.format);\n if (params.delimiter)\n s = s + '&delimiter=' + encodeURIComponent(params.delimiter);\n return s;\n };\n\n svc.Issue.prototype.isOverdue = function(){\n var duedate = dayjs(this.duedate);\n var now = (this.loan_type === 'daily' ? dayjs().startOf('day') : dayjs());\n return duedate.isBefore(now);\n };\n svc.Issue.prototype.renewedToday = function(){\n if(! this.lastreneweddate) return false;\n var lrd = new Date(this.lastreneweddate);\n var d = new Date();\n return lrd.toDateString() == d.toDateString();\n };\n\n svc.Hold.prototype.statusDateSort = function(){\n return ({W:'0',T:'1',S:'3'}[this.found]||'2')+this.reservedate;\n };\n\n return svc;\n\n}])\n\n.factory('kwOverdriveTitleModel', [\"$http\", \"$q\", \"userService\", \"alertService\", function( $http, $q, userService, alertService ){\n\n var odFormats = {\n // these are likely no longer needed, as user now only selects format\n // within OD's interface. remove when this is certain.\n \"ebook-kindle\": {\n label: \"Kindle Book\",\n type: \"ebook\" },\n \"ebook-overdrive\": {\n label: \"OverDrive Read eBook (browser-based)\",\n type: \"ebook\" },\n \"ebook-epub-adobe\": {\n label: \"DRM protected Adobe EPUB eBook\",\n type: \"ebook\" },\n \"ebook-epub-open\": {\n label: \"DRM free Open EPUB eBook\",\n type: \"ebook\" },\n \"ebook-pdf-adobe\": {\n label: \"DRM protected Adobe PDF eBook\",\n type: \"ebook\" },\n \"ebook-pdf-open\": {\n label: \"DRM free Open PDF eBook\",\n type: \"ebook\" },\n \"ebook-mediado\": {\n label: \"MediaDo Reader eBook (browser-based)\",\n type: \"ebook\" },\n \"periodicals-nook\": {\n label: \"NOOK periodicals\",\n type: \"ebook\" },\n \"audiobook-overdrive\": {\n label: \"OverDrive Listen audiobook (browser-based)\",\n type: \"audio\" },\n \"audiobook-mp3\": {\n label: \"DRM free MP3 audiobook\",\n type: \"audio\" },\n \"video-streaming\": {\n label: \"DRM protected streaming video file\",\n type: \"video\" },\n };\n var describe_format = function(code){\n return {\n code: code,\n label: odFormats[code].label,\n type: odFormats[code].type\n };\n };\n\n return function(uuid, data){\n this.id = uuid;\n this.available = false;\n this.checkedOutToMe = null;\n this.downloadRedirect = null;\n this.reservedByMe = false;\n this.fetching = true;\n this.error = null;\n this.authRequired = false;\n this.initialized = false;\n this.sampleUri = (data||{}).sampleUri;\n this.toomany = {}; // patron over circ limits.\n\n var self = this;\n\n this.canReserve = function(){\n return !this.available && !this.reservedByMe && !this.toomany.holds;\n };\n\n this.handleError = function(rsp){\n console.warn(rsp);\n var e = rsp.data;\n if(rsp.status==403 || rsp.status==404){\n self.authRequired = true;\n if(rsp.statusText == 'Invalid credentials')\n alertService.add({msg: \"Incorrect password. Please try again.\", type: \"failure\"});\n\n } else {\n if(e.errorCode==\"PatronHasExceededCheckoutLimit\"){\n self.updateStatus();\n } else {\n self.error = (e) ? e.message : rsp.statusText;\n }\n }\n // FIXME: We should count attempts, and display error\n self.fetching = false;\n return $q.reject( e );\n\n };\n this.removeHold = function(){\n this.fetching = true;\n return $http.delete('/api/patron/'+ userService.id +'/overdrive/holds/'+ self.id).then(function (response) {\n userService.overdrive.updatePatron().then( function(){\n self.reservedByMe = false;\n self.fetching = false;\n });\n }, self.handleError);\n };\n\n this.returnTitle = function(){\n this.fetching = true;\n return $http.delete('/api/patron/'+ userService.id +'/overdrive/issues/'+ self.id).then(function (response) {\n userService.overdrive.updatePatron().then( function(){\n self.reload();\n });\n }, self.handleError);\n };\n\n // var extractFormats = function(checkout){\n\n // var formats = [];\n // if(checkout.formats){\n // checkout.formats.forEach(function(fmt){\n // // these do not require 'lockin'.\n // formats.push( describe_format(fmt.formatType) );\n // });\n // }\n // if(checkout.actions && checkout.actions.format){\n // var fmt_types = checkout.actions.format.fields.filter( function(field){ return field.name==\"formatType\";});\n // if(fmt_types.length){\n // fmt_types[0].options.forEach(function(fmt_code){\n // var fmt = describe_format(fmt_code);\n // fmt.lockinRequired = true;\n // formats.push(fmt);\n // });\n // }\n // }\n // self.formatsAvailable = (formats.length) ? formats : null;\n // };\n\n this.checkout = function(format){\n this.fetching = true;\n return userService.whenAuthenticatedUser().then(function(){\n var uri = '/api/patron/'+ userService.id +'/overdrive/issues/'+ self.id ;\n var params = { op: 'checkout' };\n if(format) params.format = format;\n return $http.post(uri, $.param(params),{headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}})\n .then(function (response) {\n if((response.data||{}).errorCode)\n return self.handleError(response);\n // extractFormats(response.data);\n return userService.overdrive.updatePatron().then( function(){\n return self.updateStatus();\n });\n }, self.handleError);\n });\n };\n\n // this.getContentLink = function(fmtcode){\n\n // var fmt = this.formatsAvailable.filter(function(f){ return f.code==fmtcode; })[0];\n\n // var lockin = (!fmt.lockinRequired || this.checkedOutToMe.isFormatLockedIn) ?\n // $q.when(fmtcode) : this.checkout(fmtcode);\n // return lockin.then(function(){\n // self.fetching = true;\n // var apiurl = '/api/patron/'+ userService.id +'/overdrive/issues/'+\n // self.id + '/formats/'+ fmtcode + '/downloadlink';\n // var errorUrl = window.location.origin + '/app/error?code=od-dl-error';\n // apiurl += '?errorurl=' + encodeURIComponent(errorUrl);\n // return $http.get( apiurl ).then(function(rsp){\n // if(rsp.errorCode){\n // self.handleError(rsp);\n // return;\n // }\n\n // self.contentLink = angular.copy(rsp.data.links.contentlink);\n // self.fetching = false;\n // return(self.contentLink);\n // }, self.handleError);\n // });\n // };\n this.placeHold = function(){\n\n this.fetching = true;\n return userService.whenAuthenticatedUser().then(function(){\n// TODO: OFFER autoCheckout .\n\n return $http.post('/api/patron/'+ userService.id +'/overdrive/holds/'+ self.id).then(function (response) {\n if(response.errorCode){\n return self.handleError(response);\n }\n self.numberOfHolds++;\n return userService.overdrive.updatePatron().then( function(){\n return self.updateStatus();\n });\n }, self.handleError);\n });\n };\n\n this.updateStatus = function(){\n userService.overdrive.patron().then(function(patronData){\n if(patronData){\n self.toomany.holds = patronData.holds.totalItems >= patronData.auth.holdLimit;\n self.toomany.checkouts = patronData.checkouts.totalCheckouts >=\n patronData.auth.checkoutLimit;\n\n if(patronData.holds.holds){\n for(var i = 0; i < patronData.holds.holds.length; i++){\n var hold = patronData.holds.holds[i];\n if(hold.reserveId == self.id){\n self.reservedByMe = true;\n self.holdListPosition = hold.holdListPosition;\n }\n }\n }\n if(patronData.checkouts.checkouts){\n for(i = 0; i < patronData.checkouts.checkouts.length; i++){\n var checkout = patronData.checkouts.checkouts[i];\n if(checkout.reserveId == self.id){\n self.checkedOutToMe = true;\n self.downloadRedirect = angular.copy(checkout.links.downloadRedirect);\n // extractFormats(checkout);\n }\n }\n }\n }\n self.fetching = false;\n }, function(f){\n console.warn(f);\n });\n };\n\n var init = function( options ){\n var opt = angular.extend( {\n doAuth : false\n }, options );\n\n self.fetching = true;\n\n userService.whenAnyUserDetails().then(function(u){\n userService.overdrive.session( { prompt : opt.doAuth }).then(function(valid_session){\n userService.overdrive.patron().finally(function(odpatron){\n var params = { collection: userService.overdrive.collectionToken() };\n $http.get('/api/overdrive/holdings/'+ self.id, {params: params}).then(\n function (response) {\n var stat = response.data;\n if(!stat.errorCode){\n self.copiesAvailable = stat.copiesAvailable;\n self.copiesOwned = stat.copiesOwned;\n self.numberOfHolds = stat.numberOfHolds;\n self.available = stat.available;\n self.error = null;\n }else{\n self.available = false;\n self.error = \"Overdrive Resource Missing \" + stat.errorCode;\n }\n self.authRequired = false;\n self.updateStatus();\n }, function(rsp){\n console.log(rsp);\n return self.handleError(rsp);\n });\n\n });\n\n }).catch( function(failedsession){\n if(self.initialized) // don't error out on first load. allow user to try auth.\n self.error = 'Invalid session';\n self.authRequired = true;\n }).finally(function(){\n self.fetching = false;\n self.initialized = true;\n });\n });\n };\n init();\n this.reload = init;\n this.tryAuth = function(){\n if(userService.loggedin){\n return init({doAuth: true});\n } else {\n userService.whenAuthenticatedUser().then(function(){\n return init();\n });\n }\n };\n };\n }])\n\n .factory('kwCloudLibraryTitleModel', [\"$http\", \"$q\", \"$rootScope\", \"userService\", function ($http, $q, $rootScope, userService) {\n return function (uuid) {\n this.id = uuid;\n this.available = false;\n this.checkedOut = null;\n this.contentLink = null;\n this.reserved = false;\n this.fetching = true;\n\n this.handleError = function(e){\n console.warn(e);\n self.error = (e.message)? e.message : e;\n self.fetching = false;\n };\n\n var self = this;\n\n this.checkout = function () {\n this.fetching = true;\n\n return userService.whenAuthenticatedUser().then( function () {\n var uri = '/api/patron/'+ userService.id +'/cloudlibrary/issues/'+ self.id ;\n\n return $http.put(uri, {}).then( function (response) {\n if (response.errorCode) {\n self.handleError(response);\n return;\n }\n\n return userService.cloudlibrary.update().then( function() {\n return self.updateStatus();\n });\n }, self.handleError);\n });\n };\n\n this.returnTitle = function () {\n this.fetching = true;\n return $http.post('/api/patron/' + userService.id + '/cloudlibrary/issues/' + self.id, {}).then( function (response) {\n userService.cloudlibrary.update().then( function () {\n self.checkedOut = null;\n self.fetching = false;\n self.updateStatus();\n });\n }, self.handleError);\n };\n\n this.placeHold = function () {\n this.fetching = true;\n userService.whenAuthenticatedUser().then( function () {\n return $http.put('/api/patron/'+ userService.id +'/cloudlibrary/holds/'+ self.id, '{}').then( function (response) {\n if(response.errorCode){\n self.handleError(response);\n return;\n }\n\n return userService.cloudlibrary.update().then( function() {\n self.updateStatus();\n });\n }, self.handleError);\n });\n };\n\n this.removeHold = function () {\n this.fetching = true;\n\n return $http.post('/api/patron/' + userService.id + '/cloudlibrary/holds/' + self.id, '{}').then( function (response) {\n userService.cloudlibrary.update().then( function () {\n self.reserved = false;\n self.fetching = false;\n self.updateStatus();\n });\n }, self.handleError);\n };\n\n this.updateStatus = function () {\n return userService.cloudlibrary.promise().then( function (patronData) {\n if (patronData) {\n if (patronData.PatronCirculation.Holds) {\n var patronHolds = patronData.PatronCirculation.Holds;\n\n if (angular.isArray(patronHolds.Item)) {\n patronHolds.Item.forEach( function (hold) {\n if (hold.ItemId == self.id) {\n self.reserved = true;\n self.holdListPosition = hold.Position;\n }\n });\n }\n else {\n if (patronHolds.Item.ItemId == self.id) {\n self.reserved = true;\n self.holdListPosition = patronHolds.Item.Position;\n }\n }\n }\n\n if (patronData.PatronCirculation.Checkouts) {\n var patronCheckouts = patronData.PatronCirculation.Checkouts;\n\n if (angular.isArray(patronCheckouts.Item)) {\n patronCheckouts.Item.forEach( function (checkout) {\n if (checkout.ItemId == self.id) {\n self.checkedOut = true;\n }\n });\n }\n else {\n if (patronCheckouts.Item.ItemId == self.id) {\n self.checkedOut = true;\n }\n }\n }\n }\n\n self.fetching = false;\n return $q.when(patronData);\n });\n };\n\n var _login_listener_dereg;\n\n var init = function () {\n if (_login_listener_dereg) {\n _login_listener_dereg();\n }\n\n if ( userService.loggedin || userService.cloudlibrary.noauth_ok() ) {\n $http.get('/api/cloudlibrary/holdings/'+ self.id).then( function (response) {\n var x2js = new X2JS();\n var json_data = x2js.xml_str2json(response.data);\n var stat = json_data.ArrayOfLibraryDocumentSummary.LibraryDocumentSummary.ArrayLibraryDocumentDetail.LibraryDocumentDetail;\n\n if ( response.status == '200' && stat.ownedCopies ) {\n self.copiesAvailable = stat.ownedCopies - stat.loanCopies;\n self.copiesOwned = stat.ownedCopies;\n self.numberOfHolds = stat.holdCopies;\n self.available = stat.ownedCopies - stat.loanCopies;\n self.contentLink = 'https://ebook.yourcloudlibrary.com/library/testlibrary-document_id-' + self.id; // TODO: Get link from vendor.\n }\n else {\n self.available = false;\n self.error = \"cloudLibrary Resource Missing\";\n }\n\n self.loginRequired = false;\n self.updateStatus();\n }, function (e) {\n console.log(e);\n\n if ( e.Message == 'Authorization has been denied for this request.' ) {\n self.loginRequired = true;\n _login_listener_dereg = $rootScope.$on('loggedin', function () {\n init();\n });\n }\n else {\n self.error = e;\n }\n\n self.fetching = false;\n });\n }\n else {\n self.loginRequired = true;\n _login_listener_dereg = $rootScope.$on('loggedin', function () {\n init();\n });\n }\n };\n\n init();\n };\n }])\n\n\n// New item / mfhd / holdings factories\n.factory('bvItemSvc', [\"configService\", \"$filter\", function(configService, $filter) {\n var svc = {\n fieldDefs: [],\n fieldDef: {},\n customFields: [],\n };\n\n var customFields = [];\n configService.ItemFields.forEach(function(rec) {\n var fd = angular.extend({\n _interfaceVisibility: {'results': false, 'public': false, 'staff': false, 'edit': false},\n }, rec);\n if (fd.visibility == \"results\") {\n fd._interfaceVisibility.results = true;\n fd._interfaceVisibility['public'] = true;\n fd._interfaceVisibility.staff = true;\n fd._interfaceVisibility.edit = true;\n }\n else if (fd.visibility == \"public\") {\n fd._interfaceVisibility['public'] = true;\n fd._interfaceVisibility.staff = true;\n fd._interfaceVisibility.edit = true;\n }\n else if (fd.visibility == \"staff\") {\n fd._interfaceVisibility.staff = true;\n fd._interfaceVisibility.edit = true;\n }\n else if (fd.visibility == \"edit\") {\n fd._interfaceVisibility.edit = true;\n }\n\n svc.fieldDefs.push(fd);\n svc.fieldDef[fd.code] = fd;\n });\n\n\n // Move these into the item class itself??\n \n svc.updateDisplay = function(item) {\n if (!item._display)\n item._display = {};\n\n // Conversion to display values\n svc.fieldDefs.forEach(function(fd) {\n var field = fd.code;\n var val = item[field];\n\n if (fd.authval) {\n if (val !== undefined && val !== null) {\n val = configService.display(val, fd.type);\n }\n }\n else if (field == 'homebranch' || field == 'holdingbranch')\n val = configService.display(val, 'branch');\n else if (field == 'itemtype')\n val = configService.display(val, 'itemtype');\n else if (fd.type == 'date')\n val = $filter('kohaDate')(val);\n else if (fd.type == 'percent')\n val = $filter('percent')(val, fd.filteropts);\n else if (fd.type == 'money')\n val = $filter('kohaMoney')(val, fd.filteropts);\n else if (fd.type == 'moneystr') \n val = $filter('kohaMoneyOrStr')(val, fd.filteropts);\n\n item._display[field] = val;\n });\n\n\n if (configService.search.sortOwnBranchFirst) {\n if (configService.search_group.branch_map[item.homebranch])\n item._sorthomebranch = '0 ' + item._display.homebranch;\n else \n item._sorthomebranch = '1 ' + item._display.homebranch;\n\n if (configService.search_group.branch_map[item.holdingbranch])\n item._sortholdingbranch = '0 ' + item._display.holdingbranch;\n else \n item._sortholdingbranch = '1 ' + item._display.holdingbranch;\n }\n else {\n item._sorthomebranch = item._display.homebranch;\n item._sortholdingbranch = item._display.holdingbranch;\n }\n };\n\n svc.itemIsAvailable = function(item) {\n return !item.onloan &&\n !parseInt(item.notforloan,10) &&\n !parseInt(item.damaged,10) &&\n !parseInt(item.wthdrawn,10) &&\n !item.on_hold &&\n !item.itemlost &&\n !item.in_transit &&\n !item.shelving &&\n !item.circblocked_status &&\n !item.recalled;\n };\n\n\n svc.make = function(rawItem) {\n var item = angular.copy(rawItem);\n item.id = item.itemnumber;\n item.$hasSecondary = {staff: false, 'public': false, edit: false, results: false};\n svc.fieldDefs.forEach(function(fd) {\n // Flatten custom item fields\n if (fd.custom) {\n if (!(fd.code in item) && (fd.code in item.fields)) {\n item[fd.code] = item.fields[fd.code];\n }\n }\n // Convert dates\n if (fd.type == 'date' && item[fd.code])\n item[fd.code] = dayjs(item[fd.code]).toDate();\n\n if ((fd.code == '_availability') || item[fd.code] || (item[fd.code] === '0')) {\n if (fd.secondary == 'staff') {\n item.$hasSecondary.staff = true;\n item.$hasSecondary['public'] = true;\n }\n else if (fd.secondary == 'public') {\n item.$hasSecondary['public'] = true;\n }\n }\n });\n\n svc.updateDisplay(item);\n item.$is_available = svc.itemIsAvailable(item);\n\n return item;\n };\n\n svc.transformApiSave = function(data) {\n //# ensure non-null fields are set and fix dates.\n\n var item = angular.copy(data);\n ['dateaccessioned','replacementpricedate'].forEach(function(col){\n if(item[col]) item[col] = dayjs(item[col]).format('YYYY-MM-DD');\n });\n ['price','replacementprice'].forEach(function(col) {\n if (item[col] === '') item[col] = null;\n });\n angular.forEach(item, function(val, field){\n var fd = svc.fieldDef[field];\n if (!fd) return;\n if (fd.type == 'string' && !val) {\n item[field] = null;\n }\n if (fd.custom) {\n if(!item.fields) item.fields = {};\n item.fields[field] = item[field];\n }\n });\n return item;\n };\n\n // Item object management\n // Namespace conventions:\n // item fields are flattened on the frontend (this could be moved to OX)\n // item.foo is provided by the backend\n // item._foo is a frontend-derived field\n // item.$foo is a $resource or class method\n //\n // JS usually does camelCaps\n //\n svc.extendModel = function(itemClass) {\n itemClass.prototype.$hasFieldValue = function(field) {\n if (['itemlost','notforloan','damaged','wthdrawn'].indexOf(field)>=0) {\n return (this[field] && this[field] != \"0\") ? true : false;\n }\n else if (field == '_availability') { // Not really a field, always defined\n return true;\n }\n else {\n return this[field] ? true : false;\n }\n };\n\n itemClass.prototype.$applyDefaults = function(defaults) {\n //TODO review this\n svc.fieldDefs.forEach(function(fieldDef) {\n var defaultVal = null;\n if( (defaults||{})[fieldDef.code] ) defaultVal = defaults[fieldDef.code];\n if(fieldDef.visibility){\n if(fieldDef.authval && !defaults[fieldDef.code]){\n if(['wthdrawn','notforloan','damaged'].indexOf(fieldDef.code)>=0){\n this[fieldDef.code] = 0;\n }\n } else if(defaultVal && fieldDef.type == 'date'){\n this[fieldDef.code] = dayjs(defaultVal).toDate();\n } else {\n this[fieldDef.code] = defaultVal;\n }\n }\n // Allow add, but not mod of bibid.\n if(!this.biblionumber && defaults.biblionumber) this.biblionumber = defaults.biblionumber;\n }, this);\n };\n\n itemClass.prototype.$getSortValue = function(field){\n if(field=='enumchron'){\n return (this.serial||{}).publication_date || this.enumchron;\n } else {\n return this[field];\n }\n };\n itemClass.prototype.$copyFrom = function( srcItem ){\n angular.forEach( svc.transformApiSave( srcItem ) , function(val, field){\n this[field] = val;\n }, this);\n return this;\n }\n };\n\n\n svc.setHasFieldValue = function(items, field, testFn) {\n for (var i=0; i bN ? 1 : -1;\n } else {\n return aA > bA ? 1 : -1;\n }\n };*/\n\n var textSorter = function(a,b) {\n if (!a && !b) return 0;\n else if (!a) return -1;\n else if (!b) return 1;\n\n a = a.toLowerCase();\n b = b.toLowerCase();\n return (ab ? 1 : 0);\n };\n\n var numberSorter = function(a,b) {\n return (ab ? 1 : 0);\n };\n\n svc.sort = function(items, sortField, reverse) {\n if (!items) return items;\n var sortFunc = textSorter;\n var useDisplay = true;\n\n if (configService.search.sortOwnBranchFirst) {\n if (sortField == 'homebranch') {\n sortField = '_sorthomebranch';\n }\n else if (sortField == 'holdingbranch') {\n sortField = '_sortholdingbranch';\n }\n }\n\n var fd = svc.fieldDef[sortField];\n if (sortField == '_sorthomebranch' || sortField == '_sortholdingbranch') {\n sortFunc = textSorter;\n useDisplay = false;\n }\n else if (sortField == 'enumchron'){\n useDisplay = false;\n }\n else if (!fd) {\n sortFunc = textSorter;\n }\n else if (fd.type == 'int' || fd.type == 'decimal') {\n sortFunc = numberSorter;\n }\n else if (fd.type == 'date') {\n sortFunc = numberSorter;\n useDisplay = false;\n }\n\n // Note, this mutates the original array\n // could use slice.call(items).sort(function) instead\n if (useDisplay) {\n if (reverse)\n return items.sort(function(b,a) { return sortFunc(a._display[sortField],b._display[sortField]) });\n else\n return items.sort(function(a,b) { return sortFunc(a._display[sortField],b._display[sortField]) });\n }\n else {\n if (reverse)\n return items.sort(function(b,a) {\n return sortFunc(a.$getSortValue(sortField),b.$getSortValue(sortField));\n });\n else\n return items.sort(function(a,b) {\n return sortFunc(a.$getSortValue(sortField),b.$getSortValue(sortField));\n });\n }\n };\n\n return svc;\n}])\n\n\n//////////////////////////////////////////////////////////////////////////////////////////////////\n\n\n.factory('bvMfhdSvc', [\"bvItemSvc\", \"configService\", \"$filter\", function(bvItemSvc, configService, $filter) {\n var svc = {\n fieldDefs: [],\n fieldDef: {},\n };\n\n ['homebranch','ccode','itemtype','itemcallnumber','location','copynumber'].forEach(function(mfhdField) {\n svc.fieldDefs.push(\n svc.fieldDef[mfhdField] = bvItemSvc.fieldDef[mfhdField]\n );\n });\n\n svc.make = function(rawMfhd) {\n\n var mfhd = angular.extend({\n textual_holdings: [],\n items: [],\n visibleItemCols: {results: [], 'public': [], staff: [], edit: []},\n location: {},\n suppressed: false\n }, angular.copy(rawMfhd));\n\n if(!mfhd.id)\n mfhd._dummy = mfhd.location.homebranch||true;\n\n mfhd.$hasFieldValue = function(field) {\n if (angular.isArray(mfhd[field]))\n return (mfhd[field].length > 0) ? true : false;\n else\n return (mfhd.location[field] ? true : false);\n };\n\n svc.updateDisplay(mfhd);\n\n mfhd._textual_holdings = {\n has: (mfhd.marc_record ? mfhd.marc_record.has('86.') : false),\n expand: false,\n };\n\n\n return mfhd;\n };\n\n svc.updateDisplay = function(mfhd) {\n if (!mfhd._display)\n mfhd._display = {};\n\n // Conversion to display values\n svc.fieldDefs.forEach(function(fd) {\n var field = fd.code;\n var val = mfhd.location[field];\n\n // TODO skip if never displayed\n\n if (fd.authval)\n val = configService.display(val, fd.type);\n else if (field == 'homebranch' || field == 'holdingbranch')\n val = configService.display(val, 'branch');\n else if (field == 'itemtype')\n val = configService.display(val, 'itemtype');\n else if (fd.type == 'date')\n val = $filter('kohaDate')(val);\n else if (fd.type == 'percent')\n val = $filter('percent')(val, fd.filteropts);\n else if (fd.type == 'money')\n val = $filter('kohaMoney')(val, fd.filteropts);\n else if (fd.type == 'moneystr') \n val = $filter('kohaMoneyOrStr')(val, fd.filteropts);\n\n mfhd._display[field] = val;\n });\n\n\n if (configService.search.sortOwnBranchFirst) {\n if (configService.search_group.branch_map[mfhd.location.homebranch])\n mfhd._sorthomebranch = '0 ' + mfhd._display.homebranch;\n else \n mfhd._sorthomebranch = '1 ' + mfhd._display.homebranch;\n }\n else {\n mfhd._sorthomebranch = mfhd._display.homebranch;\n }\n };\n \n\n svc.extendModel = function(mfhdClass) {\n\n // can't apply $hasFieldValue here, since some mfhds are synthesized\n /*mfhdClass.prototype.$hasFieldValue = function(field) {\n if (angular.isArray(this[field]))\n return (this[field].length > 0) ? true : false;\n else\n return (this.location[field] ? true : false);\n };*/\n };\n\n svc.setHasFieldValue = function(mfhds, field) {\n for (var i=0; i bN ? 1 : -1;\n } else {\n return aA > bA ? 1 : -1;\n }\n };\n\n svc.sort = function(mfhds, sortField) {\n if (sortField == '_sorthomebranch')\n return mfhds.sort(function(a,b) { return (a._sorthomebranch < b._sorthomebranch) ? -1 : (a._sorthomebranch > b._sorthomebranch) ? 1 : 0; });\n else\n return mfhds.sort(function(a,b) { return naturalSorter(a._display[sortField],b._display[sortField]) });\n };\n\n return svc;\n}])\n\n\n//////////////////////////////////////////////////////////////////////////////////////////////////\n\n\n.factory('bvHoldings', [\"$q\", \"configService\", \"kwApi\", \"bvItemSvc\", \"bvMfhdSvc\", function($q, configService, kwApi, bvItemSvc, bvMfhdSvc){\n\n // container for mfhds and items.\n\n var Holdings = function(bib, mfhds, items ){\n var svc = this;\n\n svc.bib = bib;\n svc.selectedItems = {};\n\n // items and mfhds are arrays of $resource objects.\n // iface controls visibleItemCols.\n\n // singular is the object, plural is the array\n svc.items = []; // flattened, not in mfhds\n svc.mfhds = [];\n\n svc.item = {};\n svc.mfhd = {};\n\n svc.visibleItemCols = { // flat equivalent of mfhds, only 'public' is currently used\n results: [],\n 'public': [],\n staff: [],\n edit: [],\n };\n\n svc.visibleMfhdCols = []; // not interface-specific\n svc.loaded = false;\n\n function addDummyMfhd(item){\n var mfhd = svc.mfhd[item.homebranch] = bvMfhdSvc.make({\n location: { homebranch: item.homebranch, ccode: '', location: ''},\n suppressed: !!item.suppressed});\n svc.mfhds.push( mfhd );\n mfhd.items.push( item );\n }\n\n function addMfhdItem(item){\n // item should have mfhd_id if linked.\n if (item.mfhd_id) {\n if(!svc.mfhd[item.mfhd_id])\n throw \"Invalid MFHD id: \" + item.mfhd_id;\n svc.mfhd[item.mfhd_id].items.push(item);\n if (!item.suppressed)\n svc.mfhd[item.mfhd_id].suppressed = false;\n } else {\n // create dummy mfhd identified per branch.\n // TODO: Make this configurable.\n if (!svc.mfhd[item.homebranch]) {\n addDummyMfhd(item);\n } else {\n svc.mfhd[item.homebranch].items.push(item);\n if (!item.suppressed)\n svc.mfhd[item.homebranch].suppressed = false;\n }\n }\n }\n svc.lastMod = 0;\n svc.modified = function(ts){\n // simplifies watch, assuming mods to items call this.\n if(ts && ts != svc.lastMod){\n svc.lastMod = ts;\n svc._updateItemFieldVisibility();\n svc._updateMfhdFieldVisibility();\n }\n return svc.lastMod;\n }\n svc.clearModState = function(){\n // states: [editing|edited|added|deleting]\n angular.forEach(svc.item, function(item){ delete item._saveStatus; });\n }\n\n\n // (re)build the holdings object, set column visibility, and perform initial sort\n svc.init = function(mfhds, items){\n svc.items = []; // flattened, not in mfhds\n svc.mfhds = [];\n\n svc.item = {};\n svc.mfhd = {};\n\n items.forEach(function(item) {\n svc.items.push(\n svc.item[item.id] = angular.copy(item)\n );\n });\n\n mfhds.forEach(function(mfhd) {\n mfhd.suppressed = !!mfhd.items.length;\n svc.mfhds.push(\n svc.mfhd[mfhd.id] = angular.copy(mfhd)\n );\n angular.extend(svc.mfhd[mfhd.id], {\n visibleItemCols: {results: [], 'public': [], staff: [], edit: []},\n edit_id: 'mfhd:' + mfhd.id,\n items: [],\n });\n\n mfhd.items.forEach(function(itemId) {\n if (svc.item[itemId])\n svc.item[itemId].mfhd_id = mfhd.id;\n });\n });\n\n svc.items.forEach(function(item) {\n addMfhdItem(item);\n });\n\n svc._updateItemFieldVisibility();\n svc._updateMfhdFieldVisibility();\n\n svc.sortItems();\n svc.sortMfhds();\n svc.loaded = true;\n return svc;\n\n //console.dir({items: svc.items, mfhds: svc.mfhds});\n };\n\n // Set item column visibility in each interface, overall and within each mfhd group\n svc._updateItemFieldVisibility = function() {\n // Flat items\n ['results','public','staff','edit'].forEach(function(iface) {\n svc.visibleItemCols[iface].length = 0; });\n\n bvItemSvc.fieldDefs.forEach(function(fd) {\n if (!bvItemSvc.setHasFieldValue(svc.items, fd.code, false)) {\n return;\n }\n\n ['results','public','staff','edit'].forEach(function(iface) {\n if (fd._interfaceVisibility[iface]) {\n svc.visibleItemCols[iface].push(fd.code);\n }\n });\n });\n\n // mfhds\n svc.mfhds.forEach(function(mfhd) {\n ['results','public','staff','edit'].forEach(function(iface) {\n mfhd.visibleItemCols[iface] = [];\n });\n\n var excludeMfhdLocation = !configService.search.alwaysDisplayLocData && function(f,v) {\n return (!(f in mfhd.location) || (v != mfhd.location[f]));\n };\n\n bvItemSvc.fieldDefs.forEach(function(fd) {\n if (!bvItemSvc.setHasFieldValue(mfhd.items, fd.code, excludeMfhdLocation)) {\n return;\n }\n\n ['results','public','staff','edit'].forEach(function(iface) {\n if (fd._interfaceVisibility[iface]) {\n mfhd.visibleItemCols[iface].push(fd.code);\n }\n });\n });\n });\n };\n\n // Set mfhd column visibility in each interface, overall and within each mfhd group\n svc._updateMfhdFieldVisibility = function() {\n // FIXME: Allow user to turn off automatic branch grouping.\n svc.visibleMfhdCols = ['homebranch'];\n\n var showMfhdCols = false;\n svc.mfhds.forEach(function(mfhd) {\n if (mfhd.id) showMfhdCols = true;\n });\n if (!showMfhdCols) return;\n\n bvMfhdSvc.fieldDefs.forEach(function(fd) {\n if (!bvMfhdSvc.setHasFieldValue(svc.mfhds, fd.code)) {\n return;\n }\n\n if (fd._interfaceVisibility['public']) {\n if (fd.code != 'homebranch') {\n svc.visibleMfhdCols.push(fd.code);\n }\n }\n });\n };\n\n // reload and reinitialize\n svc.refresh = function(){\n console.warn('refreshing holdings');\n var bibid = svc.bib.id;\n svc.loaded = false;\n var items = kwApi.Item.workItems({id: bibid });\n var mfhds = kwApi.Mfhd.workMfhds({id: bibid});\n\n return $q.all([items.$promise, mfhds.$promise]).then(function(p){\n return svc.init(mfhds, items);\n });\n };\n\n // Removes mfhd from holdings object. Caller is responsible for $deleting.\n svc.rmMfhd = function(id){\n if(svc.mfhd[id].items.length){\n console.warn(\"cannot delete mfhd with items\");\n return;\n }\n var n = svc.mfhds.indexOf(svc.mfhd[id]);\n delete svc.mfhd[id];\n if (n>=0) svc.mfhds.splice(n,1);\n\n\n };\n\n // Removes item from holdings object. Caller is responsible for $deleting.\n svc.rmItem = function(id){\n var item = svc.item[id];\n\n try {\n // Remove from parent mfhd\n var mfhd = (item.mfhd_id) ? svc.mfhd[item.mfhd_id] : svc.mfhd[item.homebranch];\n if( !mfhd ) throw \"No MFHD record for item.\";\n\n var mfhdIdx = mfhd.items.indexOf(item);\n if(mfhdIdx>=0)\n mfhd.items.splice(mfhdIdx,1);\n else\n throw \"Not in mfhd.\";\n\n // Remove from items\n var n = svc.items.indexOf(item);\n if (n>=0) svc.items.splice(n,1);\n else throw \"No match for item. \";\n\n delete svc.item[id];\n svc.modified( item.timestamp + '.0' ); // ensures change in value.\n } catch (e) {\n console.warn(e + \"Reloading holdings.\");\n svc.refresh();\n }\n };\n\n svc.addItem = function ( items ){\n if( !angular.isArray(items) ) items = [ items ];\n var ts = ''; // str cmp.\n items.forEach(function(item){\n if(svc.item[item.id]){\n console.warn( \"Item exists: \" + item.id);\n return;\n }\n var itemCopy = angular.copy(item);\n svc.items.push( svc.item[item.id] = itemCopy );\n // assume item has already been $relinkMfhded\n addMfhdItem( itemCopy );\n\n if(itemCopy.timestamp > ts) ts = itemCopy.timestamp;\n });\n svc.modified( ts );\n }\n\n\n // Sort items (within mfhds and flat) according to sort field.\n svc.sortItems = function(sortField){\n if (!sortField) {\n sortField = svc.bib.isSerial ? 'enumchron' : 'dateaccessioned';\n }\n\n svc.mfhds.forEach(function(mfhd) {\n bvItemSvc.sort(mfhd.items, sortField);\n });\n\n bvItemSvc.sort(svc.items, sortField);\n };\n\n // Sort mfhds items according to sort field.\n svc.sortMfhds = function(sortField) {\n if (!sortField) {\n sortField = '_sorthomebranch';\n }\n bvMfhdSvc.sort(svc.mfhds, sortField);\n };\n\n // True if mfhd has value in this field (deprecated?)\n svc.hasMfhdData = function(field){\n return bvMfhdSvc.setHasFieldValue(svc.mfhds, field);\n };\n\n svc.itemCount = function(mfhd_id) {\n if (mfhd_id)\n return svc.mfhd[mfhd_id].items.length;\n else\n return svc.items.length;\n };\n\n svc.mfhdCount = function() {\n return svc.mfhds.length;\n };\n\n svc.realMfhdCount = function() {\n return svc.mfhds.filter(function(mfhd) { return mfhd.id }).length;\n };\n\n svc.unsuppressedMfhdCount = function() {\n return svc.mfhds.filter(function(mfhd) { return !mfhd.suppressed }).length;\n };\n\n\n // Hide items that do not match item-level faceting constraints\n svc.applyItemFacetConstraints = function(constraints) {\n\n svc.someItemsFacetHidden = false;\n\n svc.items.forEach(function(item) {\n var include_item = true;\n // FIXME: Due to caching, these constraints will often fail.\n // Hide non-matching items if faceted.\n // items are $resource instances, and may be mutated elsewhere.\n Object.keys(constraints).forEach( function(itemField) {\n if (!bvItemSvc.fieldDef[itemField]) return;\n console.log( constraints[itemField]);\n console.log( itemField + ' -> ' + item[itemField]);\n if ((constraints[itemField]||[]).indexOf(item[itemField])<0) {\n include_item = false;\n console.log('MATCH');\n }\n });\n\n if (!include_item) {\n item._facet_hidden = true;\n svc.someItemsFacetHidden = true;\n } else {\n item._facet_hidden = false;\n }\n });\n\n svc.mfhds.forEach(function(mfhd) {\n mfhd._facet_hidden = false;\n if (mfhd.items.length && mfhd.items.every(function(i){ return i._facet_hidden; })){\n mfhd._facet_hidden = true;\n }\n });\n };\n\n // Deselect items that are not compatible with selected items (Infomart; may be deprecated)\n svc.filterItemSelection = function(id) {\n if (!svc.selectedItems[id]) return;\n\n var item = svc.item[id];\n var selectionProgram = item.order_type + ' ' + item.program;\n\n angular.forEach(svc.selectedItems, function (val, key) {\n if (!svc.item[key]) return;\n\n var selectedItem = svc.item[key];\n var selectedSelectionProgram = selectedItem.order_type + ' ' + selectedItem.program;\n if (selectionProgram !== selectedSelectionProgram)\n svc.selectedItems[key] = false;\n });\n };\n\n\n svc.init(mfhds, items);\n };\n return Holdings;\n}])\n\n})();\n","(function () { /* Services */\n'use strict';\n\nvar module = angular.module('kohapac.services', []);\nmodule.config([\"$sceDelegateProvider\", function($sceDelegateProvider) {\n var wl = $sceDelegateProvider.resourceUrlWhitelist();\n wl.push('http://books.google.com/**', 'https://books.google.com/**');\n $sceDelegateProvider.resourceUrlWhitelist(wl);\n}]);\n\nmodule.factory('indexDataApi', [\"$rootScope\", \"$timeout\", function ($rootScope, $timeout) {\n if ( !KOHA.config.altSearch.indexdata || !KOHA.config.altSearch.indexdata.on || !KOHA.config.IndexDataMKWS ) {\n return {ready: false};\n }\n\n var svc = {};\n svc.ready = false;\n svc.query = null;\n\n svc.checkSearchReady = function () {\n if (svc.ready) {\n svc.initiateIndexSearch(svc.query);\n }\n else { // FIXME: use deferred timeouts instead of 1 second timeout loop.\n $timeout(svc.checkSearchReady, 1000);\n }\n };\n\n svc.checkDetailReady = function () {\n if (svc.ready) {\n svc.initiateIndexDetail(svc.recID)\n }\n else {\n $timeout(svc.checkDetailReady, 1000);\n }\n };\n\n svc.processIndexDataFacets = function (facets) {\n var returnedFacets = [];\n\n for (var fIndex in facets) {\n var currFacet = facets[fIndex];\n\n if ( $.isArray(currFacet) ) {\n var returned = { AvailableFacetValues : [] };\n returned.Label = fIndex == 'xtargets' ? 'Sources' : fIndex[0].toUpperCase() + fIndex.slice(1);\n returned.name = fIndex == 'xtargets' ? 'Sources' : fIndex[0].toUpperCase() + fIndex.slice(1);\n\n for ( var cIndex = 0; cIndex < currFacet.length; cIndex++ ) {\n var currChild = currFacet[cIndex];\n returned.AvailableFacetValues.push({Value : currChild.name, Id : currChild.id, Count : currChild.freq});\n }\n\n returnedFacets.push(returned);\n }\n }\n\n return returnedFacets;\n };\n\n svc.initiateIndexSearch = null;\n svc.initiateIndexDetail = null;\n\n var default_service_proxy_auth = KOHA.config.altSearch.indexdata.service_proxy_auth ? KOHA.config.altSearch.indexdata.service_proxy_auth : location.protocol + '//' + location.hostname + ( location.port ? ':' + location.port: '') + '/service-proxy-auth/';\n\n var default_service_proxy_auth_domain = KOHA.config.altSearch.indexdata.service_proxy_auth_domain ? KOHA.config.altSearch.indexdata.service_proxy_auth_domain : 'sp-mkws.indexdata.com';\n\n $('body').append('
');\n\n window.mkws_config = {\n log_level: 'error',\n service_proxy_auth: default_service_proxy_auth,\n service_proxy_auth_domain: default_service_proxy_auth_domain\n };\n\n mkws.registerWidgetType('koha', function() {\n var that = this;\n\n svc.initiateIndexSearch = function(query){\n that.team.newSearch(query);\n }\n\n svc.initiateIndexDetail = function(id){\n that.team.showDetails(id);\n }\n\n this.team.queue('stat').subscribe(function(n) {\n $rootScope.$broadcast('indexDataSearchCount', n);\n });\n\n this.team.queue('facets').subscribe(function(n) {\n svc.facets = svc.processIndexDataFacets(n);\n $rootScope.$broadcast('indexDataFacetReady', svc.facets);\n });\n\n this.team.queue('records').subscribe(function(n) {\n svc.records = n;\n $rootScope.$broadcast('indexDataRecordsReady', svc.records);\n });\n\n this.team.queue('firstrecords').subscribe(function(n) {\n $rootScope.$broadcast('indexDataSearchDone');\n });\n\n this.team.queue('complete').subscribe(function(n) {\n $rootScope.$broadcast('indexDataSearchDone');\n });\n\n this.team.queue('ready').subscribe(function(n) {\n svc.ready = true;\n });\n\n this.team.queue('pager').subscribe(function(n) {\n svc.pager = n;\n $rootScope.$broadcast('indexDataPagerReady', svc.pager);\n });\n\n this.team.queue('record').subscribe(function(data) {\n if (svc.recID == data.recid) {\n svc.record = data;\n $rootScope.$broadcast('indexDataRecordReady', svc.record);\n }\n });\n\n this.team.queue('error').subscribe( function (data) {\n $rootScope.$broadcast('indexDataError',data);\n });\n });\n\n mkws.init();\n\n svc.runIndexDataSearch = function (inputQ) {\n\n if ( inputQ == null || inputQ == '' ) {\n svc.query = '*';\n }\n else {\n svc.query = inputQ;\n }\n\n svc.checkSearchReady();\n };\n\n svc.runIndexDataDetail = function (inputID) {\n svc.recID = inputID;\n svc.checkDetailReady();\n };\n\n return svc;\n}]);\n\nmodule.factory('ebscoService', function(){\n var svc = {\n xformItem : function(str){\n return str.replace(//g,'')\n .replace(/<\\/highlight>/g,'')\n .replace(//g, '')\n .replace(/<\\/searchLink>/g,'')\n .replace(/.+?<\\/relatesTo>/g, '');\n\n },\n queryStringToHash : function QueryStringToHash (query) {\n var query_string = {};\n var vars = query.split(\"&\");\n for (var i=0;i0){\n user.id = id;\n user.loggedin = true;\n\n $timeout(function() {\n delete $http.defaults.headers.common['X-Auth-Ticket'];\n delete $http.defaults.headers.common['X-Proxy-Ticket'];\n window.BvProxyTicket = null;\n window.BvAuthTicket = null;\n }, 5000);\n } else {\n user.id = 0;\n user.loggedin = false;\n }\n user.q_user_id.resolve(user.id);\n }).catch(function(err){\n user.id = 0;\n user.loggedin = false;\n user.q_user_id.reject(err);\n });\n }\n return user.q_user_id.promise;\n };\n\n user._clearUserId = function() {\n if (user.q_user_id !== null) {\n user.q_user_id.reject();\n user.q_user_id = null;\n }\n user.id = 0;\n user.loggedin = false;\n };\n\n // Async-serialized authenticated user method\n // Does the user login dialog box if we start with an anon user\n // user.whenAuthenticatedUser(options).then(function(id) { /* user.id is now safe and >0 */ });\n\n user.whenAuthenticatedUser = function(options) {\n //console.log(\"whenAuthenticatedUser start\");\n if (user.q_authenticated_user_id === null) {\n user.q_authenticated_user_id = $q.defer();\n\n user.whenGetUserId().then(function(id) {\n //console.log(\"whenGetUserId response id = \" + id);\n if (id > 0) {\n return $q.when(id);\n }\n else {\n return user._doDialogAuth(options);\n }\n }).then(function(rv) {\n user.q_authenticated_user_id.resolve(rv);\n }, function(err) {\n // TODO - could use options here\n if (!(options && options.showAuthError === false)) {\n alertService.add({\n msg: \"You must be logged in to view this page\",\n type: \"error\"\n });\n }\n if (options && options.redirectOnFail) {\n $timeout(function() {\n $location.path(options.redirectOnFail);\n }, 100);\n }\n if (user.q_authenticated_user_id !== null) {\n user.q_authenticated_user_id.reject(err);\n user.q_authenticated_user_id = null; // Allow caller to try again\n }\n });\n }\n return user.q_authenticated_user_id.promise;\n };\n\n user._clearAuthenticatedUser = function() {\n if (user.q_authenticated_user_id !== null) {\n user.q_authenticated_user_id.reject();\n user.q_authenticated_user_id = null;\n }\n if (user.q_authenticated_user_details !== null) {\n user.q_authenticated_user_details.reject();\n user.q_authenticated_user_details = null;\n }\n }\n\n user._doDialogAuth = function(options) {\n var deferred = $q.defer();\n\n // Clear spinner if we're showing it\n if (configService.showSpinnerOnStateChange)\n loading.resolve('state');\n\n\n // downgraded Angular service: \n modalService.login().result.then( function(id){\n deferred.resolve(id);\n }, function(e) {\n deferred.reject(e);\n });\n\n // $uibModal.open({\n // backdrop: (configService.authRequired ? 'static' : false),\n // keyboard: !configService.authRequired,\n // templateUrl: '/app/static/partials/login-modal.html',\n // controller: 'LoginDlgCtrl',\n // size: ((configService.external_auth.saml||{}).iframe ? 'lg' : 'md'),\n // resolve: {\n // loginOptions: function() {\n // return options || {};\n // }\n // }\n // }).result.then(function(id) {\n // // user successfully authenticated\n // deferred.resolve(id);\n // }, function(e) {\n // deferred.reject(e);\n // });\n return deferred.promise;\n };\n\n\n // Async-serialized authenticated user details method\n // Will require user to login if they haven't already\n // user.whenAuthenticatedUserDetails(options).then(function(details) { /* user.id and user.details now safe */ });\n\n user.whenAuthenticatedUserDetails = function(options) {\n //console.log(\"WhenAuthUD ENTER\");\n if (user.q_authenticated_user_details === null) {\n //console.log(\"WhenAuthUD DETAILS\");\n user.q_authenticated_user_details = $q.defer();\n\n user.whenAuthenticatedUser(options).then(function() {\n //console.log(\"Calling _getAuthUD from whenAuthUD\");\n return user._getAuthenticatedUserDetails();\n }).then(function(rv) {\n //console.log(\"WhenAuthUD RESOLVE\");\n user.q_authenticated_user_details.resolve(rv);\n }, function(e) {\n //console.log(\"WhenAuthUD REJECT/NULL\");\n user.q_authenticated_user_details.reject(e);\n user.q_authenticated_user_details = null;\n });\n }\n return user.q_authenticated_user_details.promise;\n };\n\n function extractUserCirc (httpData) {\n return {\n fines: {\n total: httpData._embed.fines_summary.balance,\n total_accruing: httpData._embed.fines_summary.total_accruing\n },\n issues: {\n total: httpData._embed.issues_summary.total,\n overdue: httpData._embed.issues_summary.overdue\n },\n holds: {\n pending: httpData._embed.holds_summary.pending,\n waiting: httpData._embed.holds_summary.waiting,\n transiting: httpData._embed.holds_summary.transit,\n total: Number(httpData._embed.holds_summary.pending) + Number(httpData._embed.holds_summary.waiting) + Number(httpData._embed.holds_summary.transit)\n }\n };\n }\n user.updateCircData = function(){\n if(!user.details_data) return;\n return $http.get(\"/api/patron/\"+user.id ).then(function(rsp){\n user.details_data.circdata = extractUserCirc(rsp.data);\n return user.details_data.circdata;\n });\n };\n\n user._getAuthenticatedUserDetails = function() {\n //console.log(\"_getAuthUD ENTER\");\n var promises = [];\n\n // User details\n var p_details = $q.defer();\n $http.get(\"/api/patron/\"+user.id, {authRequired: true}).then(function(rsp){\n var data = rsp.data;\n // deal with _embed data\n data.circdata = extractUserCirc(data);\n data.superlibrarian = data.permit.superlibrarian;\n user.notes = data._embed.notes;\n user.login_branch = data._embed.login_branch;\n user.email = data._embed.email;\n user.username = data.userid;\n user.permit = data.permit;\n user.is_staff = user.can({catalogue: {access: '*'}});\n user.is_infomart_vendor = (data.is_infomart_vendor == 1 || data._embed.is_infomart_vendor == '1') ? true : false;\n user.has_staff_masthead = user.can({catalogue: {access: 'masthead'}});\n user.displayname = (user.is_staff && configService.showStaffUserid) ? data.userid : data.firstname; // FIXME: make configurable.\n user._initializeUserPrefs(data);\n user.can_place_holds = (data.debarred && data.debarred != \"0\") ? false : true;\n user.acl_fq = data.acl_fq;\n user.origin_userid = data._embed.origin_userid;\n user.can_switch_account = data._embed.can_switch_account;\n // FIXME - this is an awful place to put this but I can't figure out a better one\n var x = $(\"#logout-xlink-switch\");\n if (x) {\n if (user.can_switch_account)\n x.show();\n else\n x.hide();\n }\n\n var myTimeout = user.merged_prefs.application_timeout;\n if (myTimeout && myTimeout > 0) {\n var n = Math.floor(Number(myTimeout));\n // Let n be a positive integer: https://stackoverflow.com/a/10834843\n if ( n !== Infinity && String(n) === myTimeout && n > 0 ) {\n millis = 1000 * n;\n startTimer();\n }\n else {\n console.warn('Warning: timer input invalid: ' + myTimeout);\n }\n }\n\n delete data._embed;\n\n user.details_data = data;\n ['debarred', 'gonenoaddress', 'lost'].forEach(function(key){\n user.details_data[key] = KOHA.toBool(user.details_data[key]);\n\n });\n\n var missingData = (configService.UserRequiredDetails||'').split(/\\W+/)\n .filter(function(datum) {\n return (datum !== null && datum !== '' && datum !== '')\n }).filter(function(datum) {\n return (data[datum] === '' || data[datum] === null || data[datum] === undefined) ? datum : false\n });\n\n if(missingData.length){\n // FIXME: Move this into login-button.component once dlg svc is upgraded.\n $injector.get('kohaDlg').dialog({\n heading: 'Update Your Details',\n message: 'Some of your personal details are missing, please take a moment to update them. Thank you. Required:' + missingData.join(', '),\n buttons: [{val: true, label: 'Update', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}]\n }).result.then(function(rv) {\n if (rv) {\n $location.path('/app/me/details');\n }\n });\n }\n\n p_details.resolve(data); \n }).catch(function(e) {\n p_details.reject(e);\n });\n promises.push(p_details.promise);\n\n // All other user data\n var p_searches = $q.defer();\n $http.get(\"/api/patron/\"+user.id+\"/saved-searches\", {authRequired: true}).then(function(rsp) {\n var saved_search_data = rsp.data.map(function(searchmeta){ return searchmeta._embed; });\n user.savedSearches = new SavedSearchManager(saved_search_data);\n p_searches.resolve(user.savedSearches);\n }).catch(function(e){\n console.warn(e);\n p_searches.resolve(); // Don't reject, we don't care if the backend fails, just that the GET completes\n });\n promises.push(p_searches.promise);\n\n var p_all = $q.defer();\n $q.all(promises).then(function(results) {\n //console.log(\"_getAuthUD RESOLVE\");\n user.observable.next(user);\n p_all.resolve(results[0]);\n }, function(err) {\n //console.log(\"_getAuthUD REJECT\");\n p_all.reject(err);\n });\n\n return p_all.promise;\n };\n\n user.hasValidSession = function(){\n // FIXME: don't use cookies directly.\n var curSession = $cookies.get('plack_session');\n if(curSession && user.sessionid){\n return curSession == user.sessionid;\n }\n return true; // be permissive.\n };\n\n // Async-serialized user details method\n // Does not require user to login; will return anon details for user id = 0\n // user.whenAnyUserDetails(options).then(function(details) { /* user.id and user.details now safe */ });\n\n user.whenAnyUserDetails = function(options) {\n //console.log(\"whenAnyUD ENTER\");\n\n if (user.q_any_user_details === null) {\n // console.log(\"whenAnyUD DEFER\");\n user.q_any_user_details = $q.defer();\n\n user.whenGetUserId().then(function(id) {\n // console.log(\"whenAnyUD ID RESOLVE ID=\" + id);\n if (id > 0) {\n // console.log(\"Calling _getAuthUD from whenAnyUD id=\" + id);\n return user._getAuthenticatedUserDetails();\n }\n else {\n return user._getAnonUserDetails();\n }\n }).then(function(rv) {\n //console.log(\"whenAnyUD RESOLVE\");\n user.q_any_user_details.resolve(rv);\n // set sessionid...\n user.sessionid = $cookies.get('plack_session');\n }, function(e) {\n // console.log(\"whenAnyUD REJECT\");\n user.q_any_user_details.reject(e);\n // No retry possible\n });\n }\n return user.q_any_user_details.promise;\n };\n\n user._getAnonUserDetails = function() {\n var deferred = $q.defer();\n $http.get(\"/api/patron/-1\", {authRequired: false}).then(function(rsp) {\n user.permit = rsp.data.permit;\n user.merged_prefs = rsp.data.merged_prefs;\n user.acl_fq = rsp.data.acl_fq;\n user.is_staff = user.can({catalogue: {access: '*'}});\n user.has_staff_masthead = user.can({catalogue: {access: 'masthead'}});\n delete rsp.data._embed;\n //deferred.resolve(data);\n deferred.resolve({});\n user.observable.next(user);\n }).catch(function(e){\n deferred.reject(e);\n });\n return deferred.promise;\n };\n\n // Async-serialized authenticated user with permission check\n // user.whenAuthenticatedUserCan(perm,scope).then(function() { /* user.id and user.details now safe */ });\n\n user.whenAuthenticatedUserCan = function(perm, scope, options) {\n //console.log(\"WhenAuthUC enter\");\n var deferred = $q.defer();\n user.whenAuthenticatedUserDetails(options).then(function(details) {\n if (user.can(perm, scope)) {\n //console.log(\"WhenAuthUC resolve\");\n deferred.resolve(1);\n }\n else {\n // TODO - use options\n alertService.add({\n msg: \"You do not have the required permissions for this action\",\n type: \"error\"\n });\n if (options && options.redirectOnFail) {\n $timeout(function() {\n $location.path(options.redirectOnFail);\n }, 100);\n }\n //console.log(\"WhenAuthUC reject\");\n deferred.reject();\n }\n }, function(err) {\n deferred.reject(err);\n });\n return deferred.promise;\n };\n\n if(configService.overdrive){\n\n user.overdrive = (function(){\n var _sessionQ = null,\n _userPromise = null,\n error = null,\n config = angular.copy(configService.overdrive),\n collectionToken = null,\n _failCount = 0,\n clear = function(){\n _sessionQ = _userPromise = collectionToken = error = null;\n _failCount = 0;\n };\n\n var doAuth = function(password){\n // Called on login (with password) and after session expiry failures (without pw).\n // only called if password is required.\n if( ! config.require_user_pw ) return $q.reject();\n\n if(!password){\n var modalInstance = $uibModal.open({\n backdrop: false,\n templateUrl: '/app/static/partials/components/overdrive-auth.html',\n controller: [\"$scope\", \"$uibModalInstance\", function($scope, $uibModalInstance){\n $scope.username= user.username;\n $scope.auth = function(pw){\n $uibModalInstance.close(pw);\n }; // todo: add autofocus.\n }],\n windowClass: \"modal od-auth\",\n size: 'sm',\n });\n\n password = modalInstance.result;\n }\n\n return $q.when(password, function(pw){\n return $http.post('/api/overdrive/session',{password:pw});\n }, function(no_pw){\n return $q.reject(\"password_modal_closed\");\n\n }).then(function(success){\n return success;\n });\n\n };\n\n var getSession = function (options) {\n\n var opt = angular.extend( {\n prompt : false,\n password : null\n }, options );\n\n var redoAuth = (opt.password) ? true : false;\n\n if( _sessionQ && ! redoAuth ){\n // excuse the peeks at internals...\n var state = { 1 :'resolved', 2: 'rejected' }[_sessionQ.promise.$$state.status];\n\n if(!state){ /// i.e. pending.\n $log.debug('OD|', 'returning pending promise');\n return _sessionQ.promise;\n } else if ( state == 'resolved') {\n var expires = (_sessionQ.promise.$$state.value||{}).expires;\n if(new Date(expires) > new Date()){\n $log.debug('OD|', 'returning resolved promise');\n return _sessionQ.promise;\n } else {\n $log.debug('OD|', 'ODSESSION EXPIRED');\n // getting this in safari excessively. [rch]\n }\n\n } else { // i.e. rejected.\n\n if(!config.require_user_pw || !(opt.prompt || opt.password)){\n return _sessionQ.promise;\n }\n\n }\n }\n\n if(_failCount > 4){\n console.log('Too many Overdrive authentication failures. Giving up.');\n return $q.reject('Failed to establish session.');\n }\n\n _sessionQ = $q.defer();\n\n user.whenGetUserId().then(function(userid){\n\n if(config.require_user_pw && userid > 0 && ( opt.prompt || opt.password )){\n // on firstCall, always do $http.get.\n return doAuth( opt.password );\n } else {\n return $http.get('/api/overdrive/session').then(function(success){\n return success; // i.e. returns expires time.\n }, function(fail){\n\n return $q.reject(fail);\n });\n }\n }).then(function(auth_rsp){\n $q.when(_userPromise).finally(function(){\n $log.debug('OD|', 'Nullifying overdrive _userPromise ');\n _userPromise = null;\n });\n _sessionQ.resolve( auth_rsp.data );\n return auth_rsp;\n }, function(err){\n _failCount++;\n _sessionQ.reject({ error: 'badcredentials' });\n\n });\n return _sessionQ.promise;\n\n };\n\n // getSession on load.\n user.whenGetUserId().then(function(userid){\n getSession() // won't prompt first time.\n .then(function(){\n $http.get('/api/overdrive/library').then(function(rsp){\n // fixme: watch out for race condition with updatePatron, which also updates collectionToken.\n if(rsp.data.collectionToken){\n collectionToken = rsp.data.collectionToken;\n }\n });\n });\n });\n\n var updatePatron = function(){\n\n return $q.when(_userPromise).finally( function(){\n _userPromise = user.whenGetUserId().then(function(userid){\n if(userid){\n // FIXME: If error, we shouldn't bother updating.\n return user.whenAuthenticatedUser().then(function(){\n return getSession().then( function(session){\n return $http.get('/api/patron/'+user.id+'/overdrive').then(function(response) {\n var _data = response.data;\n collectionToken = _data.auth.collectionToken;\n return _data;\n }, function(e){\n console.warn(e);\n error = e;\n return $q.reject(e);\n });\n }, function(){\n $log.warn('OD|', \"sessionPromise rejected...\" );\n return $q.reject('No session');\n });\n });\n\n } else {\n return $q.reject( 'Anonymous' );\n }\n });\n\n return _userPromise;\n });\n\n };\n return {\n patron : function(){\n return (_userPromise) ? _userPromise : updatePatron();\n },\n updatePatron : updatePatron,\n loginAuth: function(pw){\n // invalidate the user and the session immediately.\n _userPromise = _sessionQ = null; // probably rather fragile.\n getSession( { password: pw } );\n }, // exposed for login.\n session : getSession,\n clear : clear,\n collectionToken: function(){\n return collectionToken;\n // return this.patron().finally( function(p){ return collectionToken; });\n }\n };\n })();\n }\n\n if (configService.cloudlibrary) {\n user.cloudlibrary = ( function () {\n var _data = null;\n var _firstPromise = null;\n var error = null;\n var noauth_ok = true;\n\n var update = function () {\n var promise;\n\n if (user.id) {\n promise = user.whenAuthenticatedUser().then( function () {\n return $http.get('/api/patron/'+user.id+'/cloudlibrary').then( function (response) {\n var x2js = new X2JS(); // eslint-disable-line no-undef\n _data = x2js.xml_str2json(response.data);\n return _data;\n }, function(e){\n console.error(e);\n error = e;\n });\n });\n }\n else {\n promise = $q.when(null);\n }\n\n if (!_firstPromise) {\n _firstPromise = promise;\n }\n\n return promise;\n };\n\n return {\n promise : function() {\n return (_data) ? $q.when(_data) : (_firstPromise) ? _firstPromise : update();\n },\n update : update,\n clear : function () {\n _data = _firstPromise = error = null;\n noauth_ok = true;\n },\n noauth_ok : function(){ return noauth_ok; }\n };\n })();\n }\n\n var timeout, millis = 60000;\n\n var startTimer = function () {\n $timeout.cancel(timeout); // Protect against multiple calls on login.\n timeout = $timeout(onExpires, millis);\n $(document).on(\"mousemove keypress\", onActivity);\n }\n\n var onExpires = function () {\n $(document).off(\"mousemove keypress\", onActivity);\n onIdle();\n }\n\n var onActivity = function () {\n $timeout.cancel(timeout);\n $(document).off(\"mousemove keypress\", onActivity);\n timeout = $timeout(startTimer, 1000); // Wait a second to avoid hammering the system.\n }\n\n var onIdle = function () {\n // TODO: may be useful to have a countdown warning before:\n user.logout();\n }\n\n // ==================================================================================================\n // This hasn't been refactored yet so it's still a bit of a mess\n user.login = function(username, password, branch){\n var params = {login: username, password: password};\n if(branch) params.branch = branch;\n\n// FIXME: Promise below never resolves if $get is authRequired but we're not authenticated.\n\n var rv_deferred = $q.defer();\n // Wait for any pending auth to complete before changing promises\n //console.log(\"Calling whenAnyUD from login\");\n user.whenAnyUserDetails().then(function() {\n //console.log(\"whenAnyUD completed\");\n //user.q_user_id = $q.defer();\n setTicket();\n $http.post(\"/api/login\", $.param(params),{headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}})\n .then(function(rsp){\n var id = parseInt(rsp.data.uri.substr(12), 10);\n if(id>0){\n user.id = id;\n user.loggedin = true;\n }\n else {\n user.id = 0;\n user.loggedin = false;\n }\n\n if (user.q_user_id !== null) {\n user.q_user_id.reject();\n user.q_user_id = null;\n }\n if (user.q_any_user_details !== null) {\n user.q_any_user_details.reject();\n user.q_any_user_details = null;\n }\n\n //user.q_user_id.resolve(user.id);\n if(user.overdrive) user.overdrive.loginAuth(password);\n user.whenAnyUserDetails().then(function() {\n $rootScope.$broadcast('loggedin');\n rv_deferred.resolve(user.id);\n if(user.cloudlibrary) {\n user.cloudlibrary.update();\n $state.reload();\n }\n }, function(err) {\n $rootScope.$broadcast('loggedin');\n rv_deferred.reject(err);\n });\n }, function(data, status) {\n user.id = 0;\n user.loggedin = false;\n //user.q_user_id.reject(status);\n rv_deferred.reject(status);\n });\n });\n return rv_deferred.promise;\n };\n\n user.logout = function(){\n delete $http.defaults.headers.common['X-Auth-Ticket'];\n delete $http.defaults.headers.common['X-Proxy-Ticket'];\n window.BvProxyTicket = null;\n window.BvAuthTicket = null;\n\n $timeout.cancel(timeout);\n\n $(document).off(\"mousemove keypress\", onActivity);\n\n $http.post(\"/api/logout\").catch(function(data, status){\n console.warn(\"Failed to log out... status \"+ status);\n }).finally(function(data,status){\n user.clear();\n $rootScope.$broadcast('loggedout');\n $state.go('home');\n });\n\n };\n user.clear = function() {\n user._clearUserId();\n user._clearAuthenticatedUser();\n this.id = 0;\n this.username = '';\n this.displayname = '';\n this.loggedin = false;\n this.is_staff = false;\n this.has_staff_masthead = false;\n this.can_place_holds = false;\n this.login_branch = undefined;\n this.permit = null;\n this.q_any_user_details = null;\n var promise = this.whenAnyUserDetails(); // i.e. anon\n // FIXME - either block search until user detail loads\n // or provide a default acl_fq to block all caveats\n this.acl_fq = null;\n if(this.cloudlibrary) this.cloudlibrary.clear();\n if(this.overdrive) this.overdrive.clear();\n $rootScope.$broadcast('clearUserData'); // All other services should clear.\n\n promise.then( (v) => {\n this.observable.next(user);\n return v;\n });\n\n return promise;\n };\n\n user.externalAuthInit = function() {\n user._clearUserId();\n user._clearAuthenticatedUser();\n this.id = 0;\n this.username = '';\n this.displayname = '';\n this.loggedin = false;\n this.is_staff = false;\n this.has_staff_masthead = false;\n this.can_place_holds = false;\n this.login_branch = undefined;\n this.permit = null;\n this.q_any_user_details = null;\n console.log(\"CALLING\");\n return user.whenAnyUserDetails().then(function(e) {\n $rootScope.$broadcast('loggedin');\n if(user.cloudlibrary) {\n user.cloudlibrary.update();\n $state.reload();\n }\n });\n };\n\n user.changePass = function(password,new_password){\n var self = this;\n // FIXME: use ssl\n\n return $http.post(\"/api/patron/\"+self.id, $.param({op: 'chpass', new_password: new_password, current_password: password}),\n {headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, authRequired: true})\n .then(function(rsp){\n self.id = rsp.data.id;\n self.loggedin = true;\n });\n };\n // \"setPrefs\" is semantically confusing, \n user.setAllPrefs = function(new_prefs) {\n var self = this;\n var enc = JSON.stringify(new_prefs);\n return $http.post(\"/api/patron/\" + self.id, $.param({op: 'set-prefs', prefs: enc}),\n {headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, authRequired: true})\n .then(function(rsp) {\n self._initializeUserPrefs(rsp.data);\n });\n };\n user.addPrefs = function(add_prefs) {\n var self = this;\n var old_prefs = angular.copy(self.prefs);\n angular.forEach(add_prefs, function(val, key) {\n old_prefs[key] = val;\n });\n return self.setAllPrefs(old_prefs);\n };\n\n\n user.getPref = function(key) {\n return this.merged_prefs[key];\n };\n\n user.setPref = function(key, val) {\n var self = this;\n if (!self.loggedin && self.prefs)\n return;\n\n self.prefs[key] = val;\n self.merged_prefs[key] = val;\n var enc = JSON.stringify(self.prefs);\n return $http.post(\"/api/patron/\" + self.id, $.param({op: 'set-prefs', prefs: enc}),\n {headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, authRequired: true})\n .then(function(rsp) {\n self._initializeUserPrefs(rsp.data);\n });\n };\n\n // prefs is the user's own userprefs, merged_prefs is merged with category and role prefs\n user._initializeUserPrefs = function(data) {\n var self = this;\n self.prefs = data.prefs;\n self.role_prefs = data.role_prefs;\n self.merged_prefs = data.merged_prefs;\n \n // I don't really want to clutter up alertService with circular dependencies, so keeping this here for now\n if ('message_popup_duration' in self.merged_prefs) {\n alertService.setTimeout(self.merged_prefs.message_popup_duration);\n }\n else {\n self.merged_prefs.message_popup_duration = alertService.getTimeout();\n }\n };\n\n // user.can(foo) if user has foo\n // user.can({foo: 'bar'}) if user has foo.bar or foo\n // user.can({foo: {bar: 'rag'}}) if user has foo.bar.rag, foo.bar, or foo\n // user.can({foo: {bar: *}}) if user has foo.bar., foo.bar, or foo\n // user.can({foo: {bar: 'baz'}}, 'catsource=quux') if user has foo.bar.baz for catsource=quux (or unscoped)\n // user.can({foo: {bar: 'baz'}}, '*') if user has foo.bar.baz for ANY scope OR unscoped\n\n user.can = function(perm, scope) {\n var self = this;\n if (!self.permit || perm === null)\n return false;\n\n // Note, don't explicitly check for superlibrarian\n // The backend will fill in all level-1 permissions in the closure\n // and we shouldn't be returning true for nonexistent permissions\n var permitNode = 0;\n if(typeof(perm)=='string' && perm.indexOf('.')>-1){\n perm = perm.split('.').reduceRight(function(acc,cv){\n // return { [cv] : acc };\n var o={}; o[cv] = acc; return o;\n });\n }\n while (perm) {\n\n if (typeof(perm) == 'object') {\n var key = Object.keys(perm)[0];\n if (permitNode === 0)\n permitNode = self.permit[key];\n else\n permitNode = permitNode[key];\n\n if (permitNode === true)\n return true;\n else if (!permitNode)\n return false;\n\n perm = perm[key];\n }\n else if (perm === '*') {\n return (typeof(permitNode) === 'object' ? true : false);\n }\n else {\n if (permitNode === 0)\n permitNode = self.permit[perm];\n else\n permitNode = permitNode[perm];\n\n if(permitNode === true)\n return true;\n else if (typeof(permitNode) == 'undefined')\n return false;\n else\n perm = permitNode[perm];\n }\n }\n\n if (scope && permitNode) {\n if (scope === '*') {\n return true;\n }\n else {\n return (permitNode[scope] === true ? true : false);\n }\n }\n return false;\n };\n\n user.canInBranch = function(perm, branch) {\n return user.can(perm, \"branch=\"+branch)\n || (branch == user.login_branch && user.can(perm, \"branch=my_own_branch\"))\n || (user.can(perm, \"branch=my_branch_group\") && configService.branchesOverlap(user.login_branch, branch));\n };\n\n user.updateDetails = function(userData){\n var toSend = {};\n angular.copy(userData, toSend);\n if(toSend.dateofbirth){\n toSend.dateofbirth = $filter('kohaDate')(toSend.dateofbirth);\n }\n return $http.put('/api/patron/'+this.id, JSON.stringify(toSend), {authRequired: true});\n };\n user.getProxyRelations = function(){\n // returns promise of relations data.\n var deferred = $q.defer();\n $http.get(\"/api/patron/\"+this.id+\"/relations\", {authRequired: true})\n .then(function(rsp){\n rsp.data.reverse_relations =\n rsp.data.reverse_relations.filter(function(rel){ return eval(rel.active); });\n rsp.data.reverse_relations = $filter('orderByDisplay')(rsp.data.reverse_relations,'proxy_surname');\n deferred.resolve(rsp.data);\n }, function(data){\n deferred.reject(data);\n });\n return deferred.promise;\n };\n\n user.setLoginBranch = function(branch){\n var self = this;\n return $http.post('/api/patron/'+this.id, $.param({op: 'set-branch', branch: branch}),\n {headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, authRequired: true}).\n then(function(){\n self.login_branch = branch;\n $rootScope.$broadcast('userSetBranch');\n });\n };\n\n user.applyProxyTicket = function(ticket) {\n $http.defaults.headers.common['X-Proxy-Ticket'] = ticket;\n user.clear().then(function() {\n return user.whenAuthenticatedUserDetails();\n }).then(function() {\n $state.go('home');\n });\n };\n\n user.exitProxy = function() {\n delete $http.defaults.headers.common['X-Proxy-Ticket'];\n $http.post('/api/logout?op=endproxy').then(function() {\n user.clear(true).then(function() {\n return user.whenAuthenticatedUserDetails();\n }).then(function() {\n $state.go('home');\n });\n }).catch(function(err) {\n console.log(err);\n });\n };\n\n user.getAccessibleBranchesAndGroups = function(perm) {\n var deferred = $q.defer();\n $http.get('/api/branch/?view=accessible&groups=1&perm='+encodeURIComponent(perm)).then(function(rsp) {\n if ((typeof(rsp) === 'object') && rsp.data)\n rsp = rsp.data;\n deferred.resolve(rsp);\n }, function(err) {\n deferred.reject(err);\n });\n return deferred.promise;\n };\n\n user.setLastExecutedSearch = function(t) {\n user.lastExecutedSearch = t;\n };\n\n user.getLastExecutedSearch = function() {\n return user.lastExecutedSearch;\n };\n\n return user;\n}]);\n\nmodule.factory('BibBatchService', [\"$http\", \"$q\", \"$timeout\", \"alertService\", \"configService\", function($http, $q, $timeout, alertService, configService){\n // Service to generate templated email / print for search results set\n // or list of bibids.\n\n return {\n submit: function(batch, options){\n\n // options keys: 'op', 'recipient', 'subject'\n // options.op in ('email', 'print', 'download').\n\n // 'download' currently just yields an html doc with disposition attachment.\n // will support more formats in future.\n\n if(!options) options = { op: 'print' };\n var params = { op: (options) ? options.op : 'print' };\n\n if(typeof batch ===\"string\"){\n params.searchquery = batch;\n } else {\n // i.e. an array of bibids.\n if(!batch.length) return $q.reject();\n params.work = batch;\n }\n\n params.subject = (options.subject) ? options.subject : \"Requested data from \" + configService.pageTitle;\n\n var deferred = $q.defer();\n var configObj = {\n headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}\n };\n if(options.op == 'email'){\n params.recipient = options.recipient;\n $http.post('/api/works-batch/', $.param(params, true), configObj)\n .then(function(resp){\n alertService.add({msg: \"Email successfully sent.\", type: \"success\"});\n deferred.resolve(resp);\n }, function(rsp){\n var ermsg = \"There was a problem with your request. \";\n if(rsp==\"403 Forbidden Sender has no email address\"){\n ermsg += \" \\nYou must have an email address on file with the library to send email.\";\n deferred.reject('NO_EMAIL');\n } else {\n deferred.reject('ERROR');\n }\n alertService.add({msg: ermsg, type: \"error\", persist: true});\n }\n );\n\n } else {\n\n if(options.op=='download'){\n configObj.responseType='blob';\n }\n $http.post('/api/works-batch', $.param(params, true), configObj).then(function(rsp){\n if(options.op=='print'){\n var printWindow = window.open();\n printWindow.document.write(rsp.data);\n printWindow.document.close();\n printWindow.print();\n } else {\n // i.e. download.\n var fname_match = rsp.headers(\"content-disposition\").match(/\\sfilename=\"([^\"]+)\"(\\s|$)/);\n var fname = (fname_match) ? fname_match[1] : 'download';\n var a = document.createElement(\"a\");\n var url = window.URL.createObjectURL(rsp.data);\n a.href = url;\n document.body.appendChild(a);\n a.download = fname;\n a.click();\n $timeout(function(){\n document.body.removeChild(a);\n window.URL.revokeObjectURL(url);\n }, 200);\n\n }\n deferred.resolve();\n }, function fail(e){\n console.warn(e);\n deferred.reject();\n });\n\n }\n return deferred.promise;\n }\n };\n}]);\n\n\n\n\nmodule.factory('SavedSearchManager', [\"$location\", \"$http\", function($location, $http ){\n\n var SavedSearchManager = function(data){\n\n this.all = [];\n\n for (var i = 0; i < data.length; i++) {\n this.all.push(data[i]);\n }\n\n this.go = function(index){\n if(this.all[index]){\n var query = this.all[index].query.split('?');\n $location.path( '/app/search/' + decodeURIComponent(query[0]) ).search( query[1] ? decodeURIComponent(query[1]) : '');\n }\n };\n this.remove = function(index){\n if(this.all[index]){\n this.all[index]._deleting = true;\n var self = this;\n return $http({method: 'DELETE', url: '/api/saved-search/'+this.all[index].id, authRequired: true}).\n then(function(){\n self.all.splice(index,1);\n }, function(e){\n console.warn(e);\n self.all[index]._deleting = false;\n });\n }\n };\n this.update = function(index) {\n if (this.all[index]) {\n var self = this;\n return $http.put('/api/saved-search/' + this.all[index].id, JSON.stringify(this.all[index]), {authRequired: true});\n }\n };\n this.has = function(query){\n // prevent duplicates\n\n var url = query.asUrl();\n for (var i = 0; i < this.all.length; i++) {\n if(this.all[i].query== url) return true;\n }\n return false;\n };\n this.add = function(query, name, apiParams, override){\n // query is a SearchQuery.\n\n var url = query.asUrl();\n url = url.replace(/^\\/app\\/search\\//, '');\n var self =this;\n // FIXME: Don't send if not loggedin.\n return $http.post('/api/saved-search', $.param( {query: url, name: name, api_query: apiParams, override: override}),\n {headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, authRequired: true} )\n .then(function(rsp){\n self.all.push(rsp.data);\n return 1;\n }, function(e){\n if (e.status == 409) {\n return 0;\n }\n else {\n console.warn(e);\n return -1;\n }\n });\n };\n\n let escapeRegExp = (string) => {\n return string.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n } // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping\n\n this.defaultName = function(query){\n var name = query.q;\n var unique = -1;\n this.all.forEach(function (search) {\n var m = search.name.match(new RegExp(escapeRegExp(name) + \"\\\\s?(\\\\d+)?\"));\n if (m) {\n if (!m[1]) unique = 1;\n if (m[1] >= unique) unique = parseInt(m[1], 10) + 1;\n }\n });\n if (unique > 0) name += ' ' + unique;\n return name;\n };\n };\n return SavedSearchManager;\n\n}]);\n\nmodule.factory('bibService', [\"$http\", \"$q\", \"CacheFactory\", \"configService\", \"bvHoldings\", \"userService\", \"kwApi\", function($http, $q, CacheFactory,\n configService, bvHoldings, userService, kwApi){\n\n // see svc.put() for the bib object .\n var svc = {};\n\n var bibcache = CacheFactory('kohaBibCache', {\n maxAge: 600000,\n capacity: 999\n });\n var holdingscache = CacheFactory('kohaHoldingsCache', {\n maxAge: 300000,\n capacity: 999\n });\n\n svc.bibCacheCapacity = function() {\n var cacheInfo = bibcache.info();\n return cacheInfo.capacity;\n };\n\n var bibPromiseCache = {}; // cache promises.\n // These are deleted after resolution.\n\n\n svc.get = function(id, option){\n // returns a promise resolving to the bib instance defined in bibService.put .\n // if option { promise: false },\n // returns an empty object that populates when resolved.\n // Note, repeated calls may yield a new object if the cache expires.\n\n var opt = angular.extend({ promise: true, includeTags: false }, option);\n\n if(!id) return;\n\n var bibObj = bibcache.get(id),\n pendingPromise = bibPromiseCache[id];\n\n if( !pendingPromise && !bibObj ) {\n\n // store unresolved promise.\n bibObj = { $resolved: false };\n bibcache.put(id, bibObj );\n\n var self = this;\n // Note, this method isn't the only one that 'puts'.\n\n var embeds = \"\";\n if(opt.includeTags){\n embeds = \"?include_tags=true\";\n }\n pendingPromise = bibPromiseCache[id] =\n $http.get(\"/api/work/\"+ id + embeds).then(\n function(rsp){\n var bib = self.put(rsp.data);\n for (var key in bib){\n if(bib.hasOwnProperty(key))\n bibObj[key] = bib[key];\n }\n return bibObj;\n }, function(e){\n return $q.reject(e);\n });\n\n }\n if(opt.promise)\n return pendingPromise || $q.when(bibObj);\n else {\n return bibObj;\n }\n };\n\n svc.clearCache = function(bibid){\n if(bibid){\n holdingscache.remove(bibid);\n bibcache.remove(bibid);\n bibPromiseCache[bibid] = undefined;\n } else {\n holdingscache.removeAll();\n bibcache.removeAll();\n bibPromiseCache = {};\n }\n };\n\n // svc.item_constraints = {};\n\n svc.hasItemField = {};\n angular.forEach(configService.ItemFields, function(f) {\n if (f.custom)\n svc.hasItemField[f.code] = 2;\n else\n svc.hasItemField[f.code] = 1;\n });\n\n // XXX YYY refactor\n svc.holdings = function(bibid, useroptions){\n // returns a promise.\n\n var options = angular.extend( { cache: true }, useroptions);\n var self = this;\n var deferred = $q.defer();\n\n if(options.cache && holdingscache.get(bibid.toString())){\n deferred.resolve(holdingscache.get(bibid.toString()));\n } else {\n var items = kwApi.Item.workItems({id: bibid});\n var mfhds = kwApi.Mfhd.workMfhds({id: bibid});\n var bib = svc.get(bibid);\n\n $q.all([items.$promise, mfhds.$promise, bib]).then(function(p){\n\n var holdings = new bvHoldings(p[2], mfhds, items);\n // XXX YYY check to make sure this can't happen too late\n // holdings.applyItemFacetConstraints(svc.item_constraints);\n\n if(options.cache) holdingscache.put(bibid, holdings);\n deferred.resolve(holdings);\n });\n }\n return deferred.promise;\n };\n\n /* unused */\n // svc.getItem = function(itemid){\n // // returns an item if it's stored in the bib cache, otherwise null.\n // var bibids = holdingscache.keys();\n // for (var i = 0; i < bibids.length; i++) {\n // if(holdingscache.get(bibids[i]).items[itemid]) return holdingscache.get(bibids[i]).items[itemid];\n // }\n // return null;\n // };\n\n svc.put = function(bibdata, options){\n\n var id = parseInt(bibdata.id,10);\n // options.replace: default true. If bib exists in cache, don't overwrite it, just get it.\n var replace = (options && options.hasOwnProperty('replace') && !options.replace) ? false : true;\n\n var bib = bibcache.get(''+id);\n var depunct = function(str){ return (str||'').replace(/\\s*[\\/:\\.]?\\s*$/, ''); };\n\n bibdata.summary.full_item_count = bibdata.summary.item_count;\n //naive lostitem/suppressed handling. [FIXME]\n if (!userService.is_staff) {\n bibdata.summary.item_count = bibdata.summary.available_item_count;\n }\n if(replace || !bib){\n var formats = (bibdata.format.length==1 && bibdata.format[0]==='') ? [] : bibdata.format;\n bib = {\n id: id,\n default_cover_id: bibdata.default_cover_id,\n title: depunct(bibdata.title),\n title_ext: depunct(bibdata.title_ext),\n isbn: bibdata.isbn,\n marc: new MarcRecord(bibdata.marc),\n uri: '/app/work/' + bibdata.id,\n format: formats,\n content: bibdata.content,\n language: (bibdata.language||'').replace(/^\\s+|\\s+$/g,''),\n summary: bibdata.summary,\n geo : bibdata.geo,\n records : $.map(bibdata.uuids,function(uuid){\n var guide = $.map(bibdata.guides,function(currGuide){\n if(currGuide.indexOf(uuid) > -1) return currGuide;});\n return { uuid : uuid, GUIDE : guide === null ? null : guide[0]};\n }),\n opac_hold_policy: bibdata.opac_hold_policy,\n isSerial: formats.indexOf('serial') != -1,\n opac_hold_ok: function(lvl){\n if(!configService.RequestOnOpac || this.opac_hold_policy=='none') // Removed `|| !this.summary.item_count` per https://www.pivotaltracker.com/story/show/161283062/comments/201664012\n return false;\n if(lvl){\n return !this.opac_hold_policy || (this.opac_hold_policy==lvl);\n }\n return true;\n },\n author: function(){ return depunct( this.marc.subfield('100a') || this.marc.subfield('110a') ||\n this.marc.subfield('700a') || this.marc.subfield('710a') || '' ); },\n publicationDate: function(){ return (this.marc.has('264')) ? this.marc.subfield('264c') : \"\" ;},\n createdDate: function(){ return (this.marc.has('260')) ? this.marc.subfield('260c') : \"\" ;},\n loadDate: function(){ return (this.marc.has('260')) ? this.marc.subfield('260g') : \"\" ;},\n isDeleted: function(){ return this.marc.subfield('942n')=='2'; },\n isSuppressed: function(){ return this.marc.subfield('942n')=='1'; },\n itemsAreRenewable: function(){\n if (Number(this.summary.holds_count) && Number(this.summary.available_count) < Number(this.summary.holds_count)) {\n return false;\n }\n return true;\n }\n };\n if(bibdata._embed.tag_terms)\n bib.tags = bibdata._embed.tag_terms;\n bib.summary.available_at = bib.summary.items_at.filter(function(loc){ return loc.available_count; });\n bibcache.put(''+id,bib);\n }\n\n // remove promise from cache.\n delete bibPromiseCache[id];\n\n return bib;\n };\n\n svc.find_isbn = function(isbn){\n var k = bibcache.keys();\n for (var i = k.length - 1; i >= 0; i--) {\n if(bibcache.get(k[i]).isbn == isbn) return bibcache.get(k[i]);\n }\n\n return null;\n };\n\n svc.details_url = function(bibid){\n // return href to details view by user's is_staff prop.\n if(userService.is_staff){\n return \"/app/staff/bib/\" + bibid + '/details';\n } else {\n return \"/app/work/\" + bibid;\n }\n };\n\n return svc;\n}]);\n\n\nmodule.factory('kohaSearchSvc', [\"$state\", \"$injector\", \"configService\", \"SearchQuery\", \"$rootScope\", \"kwLuceneParser\", function($state, $injector, configService, SearchQuery, $rootScope, kwLuceneParser){\n\n // Koha's search service.\n\n var currentSearch, lastSearch;\n\n var svc = {\n currentSearch: function(search){\n if(search){\n lastSearch = angular.copy(currentSearch);\n currentSearch = search;\n $rootScope.$emit('kohaSearchSvc.searchChanged');\n } else {\n return currentSearch;\n }\n },\n clearCurrentSearch: function(){\n lastSearch = angular.copy(currentSearch);\n currentSearch = null;\n $state.params.query = null;\n $rootScope.$emit('kohaSearchSvc.searchChanged');\n },\n lastSearch: function(){\n return lastSearch;\n },\n// onSearchChange prob equiv to watching the service's currentSearch().\n onSearchChange: function(scope, fn) {\n scope.$on('$destroy', $rootScope.$on('kohaSearchSvc.searchChanged', fn));\n },\n };\n\n $rootScope.$on('loggedout', function() {\n svc.clearCurrentSearch();\n svc.lastSearch = null;\n });\n\n var check_map = function(is_open){\n var geo_on = configService.geospatialSearch && ARCHVIEW.mapLayers && ARCHVIEW.mapLayers.length > 0;\n return (is_open) ? geo_on && $injector.get('mapComptrollerSvc').status() : geo_on;\n };\n\n // legacy search interface.\n svc.triggerSearch = function(){\n // trigger search from map. ( deprecated ).\n if (check_map()) {\n var search = new SearchQuery(svc.currentSearch());\n var geoShape = $injector.get('mapComptrollerSvc')[\"geo-shape\"];\n var status = $injector.get('mapComptrollerSvc').status();\n if (geoShape !== null && status ){\n search.q = $state.params.query || '*:*';\n search.rmLimit('geo-shape', null);\n search.addLimit('geo-shape', geoShape);\n $state.go('search-results.koha', search.stateParams());\n }\n }\n };\n\n svc.doSimpleSearch = function(srch){\n // FIXME:\n // takes a new SearchQuery, and searches, retaining previous search limits if map is open.\n if(srch.constructor === Object){\n srch = new SearchQuery(srch);\n }\n var curSrch = svc.currentSearch();\n if(!srch.hasLimits() && curSrch && check_map(true)){\n for(var field in curSrch.limitfields){\n srch.addLimit(field, curSrch.limitfields[field]);\n }\n }\n srch.go();\n\n };\n\n svc.termHighlighterWithin = function(el){\n // returns watcher fcn for boolean indicating highlight state.\n return function(highlight, wasHighlit){\n if(highlight){\n try{\n var parsed_query = kwLuceneParser.parse(svc.currentSearch().q);\n var terms = kwLuceneParser.extractTerms(parsed_query).filter(\n function(term){ return term.length > 2; });\n if(terms.length)\n el.highlight(terms);\n } catch (e) {\n return;\n }\n } else if(wasHighlit){\n el.unhighlight();\n }\n };\n }\n\n return svc;\n}]);\n\n\nmodule.factory('kwBeepSvc', function(){\n\n var audio = { };\n var sound_map = {\n beep: \"/intranet-tmpl/prog/sound/beep.ogg\",\n warn: \"/intranet-tmpl/prog/sound/critical.ogg\",\n alert: \"/intranet-tmpl/prog/sound/ending.ogg\"\n };\n\n var svc = {\n\n play: function( type ){\n if(!sound_map[type]) type = 'beep';\n if(!audio[type]) audio[type] = new Audio(sound_map[type]);\n\n var j = audio[type].play();\n }\n\n };\n return svc;\n});\n\nmodule.factory('authoritySvc', [\"$http\", \"$q\", \"CacheFactory\", function($http, $q, CacheFactory) {\n var svc = {};\n\n var authcache = CacheFactory('kohaAuthCache', {\n maxAge: 600000,\n capacity: 999\n });\n\n var authPromiseCache = {};\n\n// ## FIXME - this svc used only once. removeme.\n\n svc.get = function(id) {\n if (!id) return;\n\n var authObj = authcache.get(id);\n var pendingPromise = authPromiseCache[id];\n\n if (!pendingPromise && !authObj ) {\n authObj = { $resolved: false };\n authcache.put(id, authObj);\n\n pendingPromise = authPromiseCache[id] =\n $http.get('/api/authority/'+id).then(\n function(rsp) {\n for (var key in rsp.data) {\n if(rsp.data.hasOwnProperty(key))\n authObj[key] = rsp.data[key];\n }\n return authObj;\n }, function(e) {\n return $q.reject(e);\n }\n );\n }\n\n return pendingPromise || $q.when(authObj);\n };\n\n return svc;\n}]);\n\nmodule.factory('authoritiesSvc', [\"$http\", function($http){\n var svc = {\n query : '',\n fq_authtype : '',\n fq_linked: 'link_count_i:[1 TO *]',\n field: 'auth-heading',\n sort : 'auth-heading-sort asc',\n pager : null,\n results : [],\n hits : 0,\n searching: false,\n done: false\n };\n // a result has keys:\n // summary, authid, rcn, used, authtype, marc\n\n svc.clear = function(){\n this.results = [];\n this.hits = 0;\n //this.pager = null;\n this.error = null;\n this.done = false;\n };\n\n svc.fetch = function(){\n this.clear();\n this.searching = true;\n var solrParams = {\n fq : [this.fq_linked, this.fq_authtype],\n sort : this.sort,\n };\n var headers = {};\n\n if(this.pager && this.pager.page > 1){\n headers.Range = 'records=' + this.pager.offset + '-' + this.pager.rangeEnd;\n solrParams.start = this.pager.offset;\n }\n\n var self = this;\n var query = this.field + \":(\" + this.query + \")\";\n $http.get(\"/api/index/\"+query, { \"params\" : solrParams, \"headers\": headers})\n .then(function(response){\n angular.forEach(response.data.hits, function(auth){\n self.results.push(\n {\n summary: auth._embed.summary,\n authid: auth._embed.authid,\n rcn: auth._embed.rcn,\n marc: auth._embed.marc,\n marctext: auth._embed.formatted_marc,\n used: auth.link_count,\n typecode: auth._embed.typecode\n });\n });\n\n self.hits = response.data.total_hits; // approximate\n self.pager = new KOHA.Pager({numResults: self.hits, offset: response.data.start});\n if(!self.results.length && response.data.start + response.data.hits.length < response.data.total_hits){\n // skip to next page (FIXME: this is an ugly way to do this.)\n self.pager.setPage(self.pager.page);\n self.fetch();\n }\n self.searching = false;\n self.done = true;\n\n }, function(data){\n self.error = data;\n self.searching = false;\n self.done = true;\n });\n };\n svc.toPage = function(pagenum){ // can't call this 'page' due to how angular calls it.\n this.pager.setPage(pagenum);\n this.fetch();\n };\n\n\n\n return svc;\n}]);\n\nmodule.factory('cartService', [\"bibService\", function(bibService){\n var svc = {\n bibs: [],\n dlsos: [],\n exportFlag : false,\n selected: {},\n fullview: false\n };\n\n svc.cartCapacity = function() {\n return bibService.bibCacheCapacity();\n };\n\n svc.cartCount = function(){\n return svc.bibs.length + svc.dlsos.length;\n };\n\n svc.add = function(bibid){\n bibid = parseInt(bibid,10);\n if(this.bibs.indexOf(bibid) == -1){\n if ((this.bibs.length + 1) > this.cartCapacity()) { return; }\n this.bibs.push(bibid);\n }\n };\n\n svc.addDLSO = function(dlso){\n for(var index = 0; index < this.dlsos.length; index++){\n if(this.dlsos[index].uuid == dlso.uuid){\n return;\n }\n }\n // WCS doesn't have a true limit, other than server resources. We're using cartCapacity here\n // for consistency, and also to constrain a user's selections to a reasonable number.\n if ((this.dlsos.length + 1) > this.cartCapacity()) { return; }\n this.dlsos.push(dlso);\n };\n\n svc.addExportDLSOs = function(uuids){\n this.exportFlag = true;\n var viableDLSOs = [];\n for(var uIndex = 0; uIndex < uuids.length;uIndex++){\n var AddFlag = true;\n for(var dIndex = 0; dIndex < this.dlsos.length;dIndex++){\n if(this.dlsos[dIndex].uuid == uuids[uIndex]){\n AddFlag = false;\n break;\n }\n }\n if(AddFlag){\n viableDLSOs.push({uuid : uuids[uIndex]});\n }\n }\n if(viableDLSOs.length > 0)\n this.dlsos = this.dlsos.concat(viableDLSOs);\n }\n svc.removeDLSO = function(guid){\n var sv = -1;\n for(var index = 0; index < this.dlsos.length; index++){\n\n if(this.dlsos[index].uuid == guid){\n sv = index;\n }\n }\n\n if(sv != -1)\n this.dlsos.splice(sv,1);\n\n if(this.dlsos.length == 0)\n this.exportFlag == false;\n };\n\n svc.remove = function(bibid){\n var i = $.inArray(parseInt(bibid,10), this.bibs); // FIXME: where is it converted to string?\n if(i==-1) return;\n this.bibs.splice(i,1);\n delete this.selected[bibid];\n };\n svc.removeSelected = function(){\n for (var bibid in this.selected){\n if (this.selected[bibid]) this.remove(bibid);\n }\n };\n svc.removeAll = function(){\n this.bibs = [];\n this.selected = {};\n this.exportFlag = false;\n this.dlsos = [];\n };\n svc.removeAllBibs = function(){\n this.bibs = [];\n this.selected = {};\n\n };\n svc.removeAllDLSOs = function(){\n this.dlsos = [];\n this.exportFlag = false;\n };\n svc.selectAll = function(bool){\n if(bool){\n this.bibs.forEach(function(bibid){\n this.selected[bibid] = true;\n }, this);\n } else {\n this.selected = {};\n }\n };\n svc.inCart = function(bibid){\n // TODO: check performance.\n return ($.inArray(parseInt(bibid,10), this.bibs) == -1) ? false : true;\n };\n svc.num_selected = function(){\n var n = 0;\n for(var id in this.selected){\n if(this.selected[id]) n++;\n }\n return n;\n };\n return svc;\n}]);\n\nmodule.factory('kohaListsSvc', [\"userService\", \"bibService\", \"$http\", \"$q\", function(userService, bibService, $http, $q){\n var svc = {\n lists: {},\n dirty: true\n };\n\n // selected and updating look like { listid: { bibid: bool, bibid: bool ... } ... }\n\n svc.sync = function(){\n var self = this;\n var p1 = $q.defer();\n $http.get('/api/works-list').then(function(rsp){\n p1.resolve(rsp.data);\n })\n .catch(function(e) {\n p1.reject(e);\n });\n\n var p2 = $q.defer();\n\n userService.whenGetUserId().then(function(id) {\n if(userService.id){\n $http.get('/api/patron/'+userService.id+'/works-lists', {authRequired: true}).then(function(rsp){\n p2.resolve(rsp.data);\n })\n .catch(function(e) {\n p2.reject(e);\n });\n }\n else {\n p2.resolve([]);\n }\n });\n\n // These MUST be done in THIS order\n var all = $q.defer();\n p1.promise.then(function(data) {\n self.lists = [];\n data.forEach(function(listdata){\n self.lists[listdata.workslist.shelfnumber] = new KOHA.Biblist(listdata.workslist);\n // self.pub.push(listdata.workslist)\n });\n\n return p2.promise;\n }).then(function(data) {\n data.forEach(function(listdata){\n listdata.workslist.is_mine = true;\n self.lists[listdata.workslist.shelfnumber] = new KOHA.Biblist(listdata.workslist);\n // self.pub.push(listdata.workslist)\n });\n\n all.resolve(self.lists);\n }, function(e) {\n all.reject(e);\n });\n return all.promise;\n };\n\n\n svc.updateList = function(id){\n\n delete this.lists[id]._error;\n delete this.lists[id]._edit;\n if(id && !this.lists[id]._updating){\n this.lists[id]._updating = true;\n var self = this;\n var data = {};\n ['shelfname','shelfnumber','sortfield', 'is_public'].forEach(function(key){\n data[key] = self.lists[id][key];\n });\n // remove bibdata.\n data.works = this.lists[id].works.map(function(workObj){return {id: workObj.id, added_on:workObj.added_on};});\n\n return $http.put('/api/works-list/'+id, JSON.stringify(data) , {authRequired: true})\n .then(function(){\n self.lists[id]._updating = false;\n }, function(e){\n self.lists[id]._updating = false;\n self.lists[id]._error = e;\n });\n }\n };\n svc.createList = function(listdata, bibs){\n var now = new Date();\n var params = {};\n angular.copy(listdata, params);\n params.works = JSON.stringify(bibs.map(function(bib){ return {id: bib.id, added_on: now.toISOString()};}));\n params.op = 'create';\n var self = this;\n return $http.post('/api/works-list', $.param(params), {headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, authRequired: true})\n .then(function(){ self.sync(); }); // FIXME: Calling .sync() here could cause race condition if other requests are outstanding.\n\n };\n svc.addToList = function(id, bibs){\n var now = new Date();\n\n var to_update = 0;\n bibs.forEach(function(bib){\n if( ! this.lists[id].has_bib(bib.id)){\n // stringify the bib.id to maintain a consistent list of strings\n this.lists[id].works.push( {id: bib.id.toString() , added_on: now.toISOString() });\n to_update++;\n }\n }, this);\n if(to_update){\n return this.updateList(id);\n } else {\n return undefined; // FIXME: return deferred.\n }\n };\n svc.rmFromList = function(id, bibs){\n if(bibs.length){\n var self = this;\n this.lists[id].works = this.lists[id].works.filter(function(work){\n return (bibs.indexOf(1*work.id) == -1 && bibs.indexOf(\"\"+work.id) == -1);\n });\n bibs.forEach(function(bibid){\n delete self.lists[id].selectedbibs[bibid];\n });\n return this.updateList(id).then(function(rsp){\n console.log(rsp.data);\n }).catch(function(e){\n self.sync();\n });\n }\n };\n svc.deleteList = function(id){\n var self = this;\n self.lists[id].deleting = true;\n return $http({method: 'DELETE', url: '/api/works-list/'+id, authRequired: true}).\n then(function(){\n delete self.lists[id];\n });\n };\n\n svc.get_all = function(){\n var allshelves = [];\n Object.keys(this.lists).forEach(function(listid){ allshelves.push(this.lists[listid]); }, this);\n return allshelves.sort(function(a,b){ return a.shelfname.toLowerCase() > b.shelfname.toLowerCase() ? 1 : -1 ;});\n };\n\n svc.get_public = function(){\n var pubshelves = [];\n Object.keys(this.lists).forEach(function(listid){ if(!this.lists[listid].is_mine) pubshelves.push(this.lists[listid]); }, this);\n\n return pubshelves.sort(function(a,b){ return a.shelfname.toLowerCase() > b.shelfname.toLowerCase() ? 1 : -1 ;});\n // fixme: pubshelves and myshelves should perhaps be a property of the svc for better performance\n };\n svc.get_mine = function(){\n var myshelves = [];\n Object.keys(this.lists).forEach(function(listid){ if(this.lists[listid].is_mine) myshelves.push(this.lists[listid]); }, this);\n return myshelves.sort(function(a,b){ return a.shelfname.toLowerCase() > b.shelfname.toLowerCase() ? 1 : -1 ;});\n\n };\n\n // List comments\n svc.commentCreate = function(list,comment) {\n var deferred = $q.defer();\n var params = {comment: comment};\n $http.post('/api/works-list/' + list.id + '/comments', $.param(params),\n {headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, authRequired: true})\n .then(function(resp) {\n list.comments = resp.data.map(function(obj) { return obj.comment; });\n deferred.resolve(list.comments);\n }).catch(function(err) {\n deferred.reject(err);\n });\n return deferred.promise;\n };\n\n svc.commentUpdate = function(list, c) {\n var deferred = $q.defer();\n $http.put('/api/list-comment/' + c.id, JSON.stringify(c),{authRequired: true}).then(function() {\n $http.get('/api/works-list/' + list.id + '/comments').then(function(rsp) {\n list.comments = rsp.data.map(function(obj) { return obj.comment; });\n deferred.resolve(list.comments);\n }).catch(function(err) {\n deferred.reject(err);\n });\n }).catch(function(err) {\n deferred.reject(err);\n });\n return deferred.promise;\n };\n\n svc.commentDelete = function(list, c) {\n var deferred = $q.defer();\n $http({method: 'DELETE', url: '/api/list-comment/' + c.id, authRequired: true}).then(function() {\n $http.get('/api/works-list/' + list.id + '/comments').then(function(rsp) {\n list.comments = rsp.data.map(function(obj) { return obj.comment; });\n deferred.resolve(list.comments);\n }).catch(function(err) {\n deferred.reject(err);\n });\n }).catch(function(err) {\n deferred.reject(err);\n });\n return deferred.promise;\n };\n\n svc.get = function(id){\n // add bibdata.\n var shelf = this.lists[id];\n var self = this;\n if (!shelf.comments) {\n shelf.comments = [];\n $http.get('/api/works-list/' + id + '/comments').then(function(resp) {\n shelf.comments = resp.data.map(function(obj) { return obj.comment; });\n });\n }\n\n if(shelf){\n\n shelf._shownWorks = shelf.works.length || 0;\n\n shelf.works.forEach(function(bibdata, i){\n bibService.get(bibdata.id).then(function(bib){\n bibdata.bib = bib;\n bibdata.is_visible = !bib.isDeleted() && !bib.isSuppressed();\n if(!bibdata.is_visible) --shelf._shownWorks;\n }, function(err) {\n --shelf._shownWorks;\n });\n });\n }\n return shelf;\n };\n\n svc.clear_private = function(){\n // remove nonpublic lists.\n for(var id in this.lists){\n if(this.lists[id].is_public){\n delete this.lists[id].is_mine;\n } else {\n delete this.lists[id];\n }\n }\n };\n\n return svc;\n}]);\nmodule.factory('SelectionMgr', function(){\n\n // Simple hash selection manager.\n // tracks selection by id.\n // call select() with the id or the object (or array of either).\n return function SelectionMgr(key){\n if(!key) key = 'id'; // allows selection by object.\n var self = this;\n function _key(keyOrObj){\n if(angular.isObject(keyOrObj)) return keyOrObj[key];\n return keyOrObj;\n }\n this.selected = {};\n this.clear = function(thing){\n if(thing) delete self.selected[_key(thing)];\n else angular.forEach(self.selected, function(v,k){\n delete self.selected[k];\n });\n };\n this.count = function(){\n return Object.keys(self.selected).filter(\n function(id){return self.selected[id]; }.bind(this)).length;\n };\n this.select = function(things){\n if(angular.isArray(things))\n things.forEach(function(thing){ self.selected[_key(thing)] = true; }.bind(this));\n else self.selected[_key(things)] = true;\n };\n this.ids = function(){ return Object.keys( self.selected ).filter(function(k){ return self.selected[k]; })};\n };\n});\n\nmodule.factory('kohaIssuesSvc', [\"configService\", function( configService){\n\n var svc = {};\n\n svc.checkOutBarcode = function(barcode){\n var outcome = {};\n \n var id = null;\n \n //37570000422088\n $.ajax({\n type: \"GET\",\n url: '../../api/item?barcode='+barcode,\n async: false,\n cache: true,\n contentType: \"application/json\",\n headers: configService.getXSRFHeader(),\n success: function (data) {\n id = data[\"_embed\"]['item'][\"itemnumber\"]; \n },\n error: function (err) {\n outcome.status = false;\n outcome.message = \"Failed to complete checkout with provided barcode.\";\n \n }\n });\n \n if(outcome.status == false)\n return outcome;\n\n $.ajax({\n type: \"POST\",\n url: '../../api/item/' + id + '?op=issue',\n async: false,\n cache: true,\n headers: configService.getXSRFHeader(),\n success: function (data) {\n outcome.status = true;\n \n },\n error: function (err) {\n outcome.status = false;\n outcome.message = err.statusText + \" - \" + err.responseText;\n }\n });\n \n return outcome;\n };\n\n return svc;\n}]);\n\nmodule.factory('kohaCourseSvc', [\"$http\", \"bibService\", function($http, bibService){\n var svc = { courses : [] };\n // TODO: Will likely have to implement paging.\n // Or maybe filter out courses with no reserves.\n\n svc.sync = function(){\n this.loading = true;\n var self= this;\n $http.get('/api/course')\n .then(function(rsp){\n self.courses = jQuery.grep(rsp.data, function(course){ return (course.course_status == \"enabled\"); });\n self.loading = false;\n }).catch(function(e){\n console.warn(e);\n self.loading = false;\n });\n };\n svc.getCourse = function(id){\n for (var i = this.courses.length - 1; i >= 0; i--) {\n if(this.courses[i].course_id === id){\n if(this.courses[i].course_status==='enabled' && this.courses[i].reserves.length){\n this.courses[i].reserves.forEach(function(reserve){\n $http.get('/api/item/'+ reserve.itemnumber).then(function(rsp){\n reserve.bibid = rsp.data.biblionumber;\n reserve.bib = {};\n bibService.get(rsp.data.biblionumber).then(function(bib){\n console.log(bib);\n reserve.bib = bib;\n });\n });\n });\n }\n return this.courses[i];\n }\n }\n return null;\n };\n return svc;\n}]);\n\nmodule.factory('kohaReviewSvc', [\"$http\", \"userService\", function($http, userService){\n // maintain reviews for a single bib at a time.\n var svc = {\n bibid: null,\n loading: false,\n reviews: [],\n mine: null\n };\n svc.clear = function(){\n svc.bibid = null;\n svc.loading = false;\n svc.reviews = [];\n svc.mine = null;\n };\n svc.get = function(bibid){\n this.clear();\n this.loading = true;\n this.bibid = bibid;\n var self= this;\n $http.get('/api/work/'+bibid+'/reviews')\n .then(function(rsp){\n rsp.data.forEach(function(obj){\n self.reviews.push(obj.review);\n if(userService.id && obj.review.borrowernumber==userService.id){\n self.mine = obj.review;\n }\n });\n self.reviews = self.reviews.sort(function(a,b){return a.datereviewed < b.datereviewed;});\n self.loading = false;\n\n }, function(e){\n console.log(e);\n self.loading = false;\n });\n };\n svc.submit = function(review){\n if(review.review && this.bibid && userService.id){\n return $http.post('/api/work/'+this.bibid+'/reviews', $.param({text: review.review}),\n {headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, authRequired: true});\n }\n\n };\n return svc;\n}]);\n\nmodule.factory('patronRegSvc', [\"$q\", \"alertService\", \"kwApi\", function($q, alertService, kwApi){\n var svc = { loading: false };\n\n svc.getPatronApps = function(){\n var deferred = $q.defer();\n svc.loading = true;\n kwApi.PatronRegistration.getApps().$promise.then(function(r){\n svc.loading = false;\n deferred.resolve(r);\n });\n return deferred.promise;\n };\n\n svc.rejectAppSvc = function(d){\n var deferred = $q.defer();\n d.status = 'rejected';\n kwApi.PatronRegistration.rejectApp({id: d.id}, JSON.stringify(d)).$promise.then(function(){\n alertService.add({msg: \"Patron application rejected\", type: \"success\"});\n deferred.resolve(1);\n }, function(e){\n alertService.addApiError(e, \"Patron application update failed\");\n console.warn(e);\n deferred.reject();\n });\n return deferred.promise;\n };\n\n svc.approveAppSvc = function(d){\n var deferred = $q.defer();\n d.status = 'approved';\n kwApi.PatronRegistration.approveApp({id: d.id}, JSON.stringify(d)).$promise.then(function(){\n alertService.add({msg: \"Patron application approved\", type: \"success\"});\n deferred.resolve(1);\n }, function(e){\n alertService.addApiError(e, \"Patron application update failed\");\n console.warn(e);\n deferred.reject();\n });\n return deferred.promise;\n };\n\n return svc;\n}]);\n\nmodule.factory('kohaCallslipSvc', [\"$http\", \"userService\", \"alertService\", function($http, userService, alertService){\n var svc = { requests: {}, loading: false };\n svc.sync = function(){\n var self= this;\n this.loading = true;\n return $http.get('/api/patron/'+userService.id+'/callslips', {authRequired: true})\n .then(function(rsp){\n self.requests = rsp.data.map(function(obj){ return obj.callslip; });\n console.log(self.requests);\n self.loading = false;\n }, function(e){\n console.log(e);\n self.loading = false;\n });\n };\n svc.submit = function(request){\n var data = {};\n angular.copy(request, data);\n\n data.work_id = data.biblionumber;\n data.branch_id = data.pickup_branch;\n data.item_id = data.requested_itemnumber;\n if(data.not_needed_after && typeof(data.not_needed_after)=='object'){\n data.not_after = data.not_needed_after.toISOString();\n }\n data.type = data.request_type;\n data.note = data.request_note;\n data.op = 'place';\n return $http.post('/api/callslip', $.param(data),{headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, authRequired: true})\n .then(function(){\n // FIXME: API isn't working yet. return to this later.\n });\n };\n svc.cancel = function(id){\n var self =this;\n return $http({\n method: 'DELETE',\n url: '/api/callslip/'+id,\n authRequired: true,\n headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}\n }).then(function(){\n for (var i = self.requests.length - 1; i >= 0; i--) {\n if(self.requests[i].callslip_id===id){\n self.requests[i].request_status= 'canceled';\n break;\n }\n }\n alertService.add({msg: \"Request successfully canceled.\", type: \"success\"});\n }, function(why){\n alertService.addApiError(why,'Cancellation failed');\n });\n };\n svc.has_current_request = function(bibid){\n // users may only have one request at a time ona bib.\n\n };\n return svc;\n}]);\n\nmodule.factory('ratingService', [\"$http\", \"$q\", \"userService\", \"alertService\", function($http, $q, userService, alertService) {\n var svc = {\n user_cache: {},\n loaded: false,\n user_id: 0\n };\n\n svc.submit = function(rating, bibid) {\n var deferred = $q.defer();\n\n $http.post('/api/work/'+bibid + '/ratings', $.param({rating: rating}), {headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, authRequired: true})\n .then(function(resp) {\n alertService.add({msg: 'Rating updated'});\n deferred.resolve(resp);\n\n }, function(resp) {\n alertService.addApiError(resp,'Rating submmission failed');\n deferred.reject(resp);\n });\n\n return deferred.promise;\n };\n\n svc.getPatronRatings = function() {\n var deferred = $q.defer();\n\n userService.whenGetUserId().then(function(id) {\n if (!id) {\n deferred.reject();\n return;\n }\n if (id != svc.user_id) {\n svc.loaded = false;\n svc.user_cache = {};\n svc.user_id = userService.id;\n }\n\n if (svc.loaded) {\n deferred.resolve(svc.user_cache);\n }\n else {\n $http.get('/api/patron/'+userService.id+'/ratings', {authRequired: true}).then(function(rsp){\n svc.user_cache = {};\n rsp.data.forEach(function(rec) {\n svc.user_cache[rec.rating.work_id] = rec.rating.rating;\n });\n deferred.resolve(svc.user_cache);\n }, function(r) {\n deferred.reject(r);\n });\n }\n }, function(r) {\n deferred.reject(r);\n });\n\n return deferred.promise;\n };\n\n return svc;\n}]);\n\n\nmodule.factory('kohaTagsSvc', [\"$http\", \"$q\", \"userService\", \"alertService\", \"configService\", function($http, $q, userService, alertService, configService){\n var svc = {\n mytags: {},\n };\n\n svc.get = function(bibid){\n var deferred = $q.defer();\n $http.get('/api/work/'+bibid+'/tag-terms').then(function(rsp){\n deferred.resolve(rsp.data);\n }, function(e){\n deferred.reject(e);\n });\n return deferred.promise;\n };\n svc.submit = function(tagterm, bibid){\n var self= this;\n return $http.post('/api/work/'+bibid+'/tag-terms', $.param({ op: 'tag', term: tagterm }), {headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, authRequired: true} )\n .then(function(rsp){\n alertService.add({msg: (configService.TagsModeration) ? ' Tag submitted for review.' : 'Tag added!'});\n }, function(data){\n alertService.addApiError(data,'Tag submission failed');\n // TODO: remove tag from display.\n });\n };\n svc.deleteTag = function(id){\n var self = this;\n $http({method: 'DELETE',\n url: '/api/tag/'+id ,\n authRequired: true,\n headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}\n }).then(function(){\n alertService.add({msg: \"Tag successfully deleted.\", type: \"success\"});\n delete self.mytags[id];\n }, function(huh){\n alertService.addApiError(huh,'Tag deletion failed');\n });\n };\n svc.cloud = function(){\n var deferred = $q.defer();\n var maxsize = 4; // i.e. em\n $http.get('/api/tag-term').then(function(rsp){\n // api returns ordered by weight, so first result is max weight.\n // FIXME: should probably limit number returned by api.\n // this only works well for maxweight >> 4.\n if(rsp.data.length){\n var x = maxsize / parseInt(rsp.data[0].weight,10);\n deferred.resolve(rsp.data.map(function(tag){ tag.size = parseInt(tag.weight,10) * x + 1; return tag; })\n .sort(function(a,b){ return (a.term.toLowerCase() > b.term.toLowerCase()) ? 1:-1; }));\n }\n }, function(data){\n deferred.reject(data);\n });\n return deferred.promise;\n };\n\n svc.sync_mytags = function(){\n var self = this;\n this.mytags = {};\n var q = $q.defer();\n $http.get('/api/patron/'+userService.id+'/tags', {authRequired: true}).then(function(rsp){\n rsp.data.forEach(function(tag){\n // tag.bib = bibService.get(tag.biblionumber).then(function(bib){\n // tag.title = bib.title;\n // tag.title_ext = bib.title_ext;\n // // This sucks. now that promises aren't auto-unwrapped, it's getting messy.\n // });\n self.mytags[tag.tag_id] = tag;\n });\n q.resolve(self.mytags);\n });\n return q.promise;\n };\n\n return svc;\n}]);\n\nmodule.factory('kohaSuggestSvc', [\"$http\", \"$q\", \"userService\", \"alertService\", function($http, $q, userService, alertService){\n var svc = {};\n var statusmap = { ASKED: 'Requested', REJECTED: 'Rejected', ORDERED: 'On order', ACCEPTED: 'Accepted' };\n\n svc.mine = function(){\n var deferred = $q.defer();\n $http.get('/api/patron/'+userService.id+'/suggestions', {authRequired: true}).then(function(rsp){\n var suggestions = [];\n rsp.data.forEach(function(responsedata){\n responsedata.suggestion.status = statusmap[responsedata.suggestion.STATUS];\n suggestions.push(responsedata.suggestion);\n });\n deferred.resolve(suggestions);\n }, function(e){\n deferred.reject(e);\n });\n return deferred.promise;\n };\n svc.deleteSuggestion = function(id){\n return $http({method: 'DELETE', url: '/api/suggestion/'+id, authRequired: true}).then(function(){\n alertService.add({msg: \"Purchase suggestion successfully deleted.\", type: \"success\"});\n }, function(huh){\n alertService.addApiError(huh,'Purchase suggestion deletion failed');\n });\n };\n svc.submit = function(data){\n console.log(data);\n data.op = 'create';\n if(data.holdfor) data.holdfor = userService.id;\n return $http.post(\"/api/suggestion\", $.param(data), {headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, authRequired: true})\n .then(function(rsp){\n alertService.add({msg: \"Purchase suggestion successfully submitted\", type: 'success'});\n }, function(e){\n console.warn(e);\n });\n\n };\n return svc;\n}]);\n\nmodule.factory('kohaILLRequestSvc', [\"$http\", \"$q\", \"userService\", \"alertService\", \"$filter\", function($http, $q, userService, alertService, $filter){\n var svc = {};\n var statusmap = { submitted: 'Submitted', rejected: 'Rejected', ordered: 'On order', approved: 'Approved', available: 'Available' };\n\n svc.mine = function(){\n var deferred = $q.defer();\n $http.get('/api/patron/'+userService.id+'/ill-requests', {authRequired: true}).then(function(rsp){\n var requests = [];\n rsp.data.forEach(function(ent) {\n ent.request.status = statusmap[ent.request.status] || 'Error';\n if (ent.request.needed_by_date) {\n ent.request.needed_by_date = $filter('kohaDate')(ent.request.needed_by_date);\n }\n requests.push(ent.request);\n });\n deferred.resolve(requests);\n }, function(e){\n deferred.reject(e);\n });\n return deferred.promise;\n };\n\n svc['delete'] = function(id){\n return $http({method: 'DELETE', url: '/api/ill-request/'+id, authRequired: true}).then(function(){\n alertService.add({msg: \"Interlibrary loan request successfully deleted.\", type: \"success\"});\n }, function(huh){\n alertService.addApiError(huh,'Interlibrary loan request deletion failed');\n });\n };\n svc.create = function(data){\n data.op = 'create';\n if (data.needed_by_date) {\n data.needed_by_date = data.needed_by_date.toISOString();\n }\n if (data.hold_for)\n data.hold_for = userService.id;\n\n return $http.post(\"/api/ill-request\", $.param(data),\n {headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, authRequired: true}\n ).then(function(){\n alertService.add({msg: \"Interlibrary loan request submitted\", type: 'success'});\n }, function(huh){\n alertService.addApiError(huh,'Interlibrary loan request submission failed');\n console.warn(huh);\n });\n };\n svc.update = function(data) {\n return $http.put(\"/api/ill-request/\" + data.id, JSON.stringify(data), {authRequired: true}).then(function() {\n alertService.add({msg: \"Interlibrary loan request updated\", type: 'success'});\n }, function(huh){\n alertService.addApiError(huh,'Interlibrary loan request update failed');\n console.warn(huh);\n });\n };\n return svc;\n}]);\n\nmodule.factory('kohaXISBNSvc', [\"$http\", \"$q\", \"bibService\", function($http, $q, bibService){\n var svc = { loading: false };\n\n var bibs = {};\n // map of bibid to an array of bibids.\n\n svc.get = function(bibid){\n this.loading = true;\n var self = this;\n var deferred = $q.defer();\n $http.get('/api/work/'+bibid+'/xisbns').then(function(rsp){\n deferred.resolve(rsp.data.map(function(rel_id){ return bibService.get(rel_id); }));\n self.loading = false;\n }, function(){\n self.loading = false;\n });\n return deferred.promise;\n\n };\n return svc;\n\n}]);\n\nmodule.factory('alertService', [\"$timeout\", \"$sce\", function($timeout, $sce){\n var alerts = [];\n var curId = 0;\n var svc = {\n timeout: 7100\n };\n\n svc.addApiError = function(err, context) {\n var str;\n if ((typeof(err) === 'object') && err.data) {\n str = err.data;\n }\n else if ((typeof(err) === 'object') && err.statusText) {\n str = err.statusText;\n }\n else if (typeof(err) === 'object') {\n str = 'API Error';\n }\n else {\n str = '' + err;\n }\n\n if (context) {\n str = context + ': ' + str;\n }\n svc.add({msg: str, type: 'error'});\n };\n\n\n svc.add = function(alert){\n var self = this;\n // Can't prune by array position with mixed persistent / transient alerts\n curId++;\n // bootstrap alerts.\n // an alert should be: { type: ('error'|'success'), default 'info', msg: 'message. just text.'}\n // optional property: persist: true => won't auto expire.\n\n if (alert.type === 'error')\n alert.type = 'danger'\n else if (!alert.type)\n alert.type = 'info';\n\n alerts.push({\n id: curId,\n type: alert.type,\n class: 'alert-' + alert.type,\n msg: $sce.trustAsHtml((alert.msg)),\n link : alert.link,\n linkCallBack : alert.linkCallBack\n });\n var t = (alert.timeout > 0 ? alert.timeout : self.timeout);\n if (t && !alert.persist) {\n var id = curId;\n $timeout(function(){\n self.dismiss(id); // TODO: Add animation.\n }, t);\n }\n };\n svc.dismiss = function(id){\n for (var i=0; i bibid(s)\n\n var trigger_gbs_load = function(bib){\n\n if(!bib.isbn) return;\n if(gbs_queue[bib.isbn]) gbs_queue[bib.isbn].push(bib.id);\n else gbs_queue[bib.isbn] = [bib.id];\n\n if(!gbs_timeout){\n gbs_timeout = $timeout(function(){\n if(Object.keys(gbs_queue).length){\n var requested_isbn = angular.copy(gbs_queue);\n\n $http.jsonp(\"//books.google.com/books?bibkeys=\" + encodeURIComponent(Object.keys(requested_isbn)) +\n \"&jscmd=viewapi\").then(function(rsp){\n angular.forEach(rsp.data, function(data, isbn){\n if(!requested_isbn[isbn]) return;\n requested_isbn[isbn].forEach(function(bibid){\n covers[bibid].gbs_update(data);\n });\n delete requested_isbn[isbn];\n });\n angular.forEach(requested_isbn, function(bibs, isbn){\n bibs.forEach(function(bibid){ covers[bibid].gbs_update(null);});\n });\n }, function(e,status){\n console.warn(\"gbs failed with status \"+status);\n console.warn(e);\n angular.forEach(requested_isbn, function(bibs, isbn){\n bibs.forEach(function(bibid){ covers[bibid].gbs_update(null);});\n });\n });\n gbs_queue = {};\n }\n gbs_timeout = null;\n }, 50);\n }\n };\n\n var svc = {\n get: function(bibid){\n if(!covers[bibid]){\n var cover = new CoverImage(bibid);\n bibService.get(bibid).then(function(bib){\n if(cover.provider == 'gbs'){\n if(bib.isbn){\n trigger_gbs_load(bib);\n } else {\n cover.gbs_update(null);\n }\n }\n }, function(){\n if(configService.coverImgSource == 'gbs'){\n cover.gbs_update(null);\n }\n });\n covers[bibid] = cover;\n }\n\n return covers[bibid].promise;\n }\n };\n\n return svc;\n}])\n\n\n.service('bvSearchToPick', [\"$state\", \"$rootScope\", \"userService\", function($state, $rootScope, userService){\n\n // maintain patron id for search-to-hold and search-to-issue\n\n this.patronid = null; // patron id.\n this.action = null; // issue or hold.\n this.targetBib = null;\n var stateListener = null;\n var self = this;\n\n this.issueTo = function(p){\n\n this.patronid = p;\n this.action = 'issue';\n // listener should probably be in directive.\n\n stateListener = $rootScope.$on('$stateChangeSuccess', function(e, toState, toParams){\n if($state.includes('staff.bib')){\n self.targetBib = $state.params.biblionumber;\n } else if($state.includes('work')){\n self.targetBib = $state.params.bibid;\n } else {\n self.targetBib = null;\n }\n });\n };\n\n this.holdFor = function(p){\n this.patronid = p;\n this.action = 'hold';\n };\n\n this.cancel= function(){\n var prevPatronId = this.patronid;\n var prevAction = this.action;\n if(stateListener) stateListener();\n this.patronid = this.action = this.targetBib = stateListener = null;\n if (prevAction == 'hold') {\n $state.go('staff.patron.checkout',{borrowernumber: prevPatronId});\n }\n };\n\n this.fastAdd = function(targetBib){\n if(!this.targetBib) return;\n\n $state.go('staff.circ.fast-add', {\n biblionumber: this.targetBib,\n // op: 'save', // uncomment to create item without form step.\n // redirect: 1,\n borrowernumber: this.patronid,\n holdingbranch: userService.login_branch\n });\n this.cancel();\n };\n\n this.returnToOrigin = function() {\n // var toState = (this.action=='hold') ?\n // 'staff.patron.details' : 'staff.patron.checkout';\n $state.go( 'staff.patron.checkout', { borrowernumber: this.patronid });\n this.cancel();\n };\n\n}]);\n\n\nmodule.factory('kohaDlg', [\"$uibModal\", \"$timeout\", \"$q\", \"$injector\", \"$http\", \"$state\", \"kwApi\", \"userService\", \"bvSearchToPick\", \"configService\", \"$rootScope\", function($uibModal, $timeout, $q, $injector, $http,\n $state, kwApi, userService, bvSearchToPick, configService, $rootScope){\n\n // globally available modal dialogs, as well as a generic modal. (wait, what??)\n\n var svc = {\n\n placeHold: function(bibids, patronid){\n\n if(!userService.loggedin && configService.external_auth.saml && !configService.external_auth.saml.iframe){\n window.location.assign('/api/saml/login');\n return;\n }\n\n return userService.whenAuthenticatedUserDetails().then(function(details){\n var maybeGetPatron = patronid || bvSearchToPick.patronid;\n if(userService.can({reserveforothers: '*'}) && !maybeGetPatron ){\n // require patron selection first.\n var bvStaffDlg = $injector.get('bvStaffDlg');\n maybeGetPatron = bvStaffDlg.patronSelect();\n }\n return $q.when(maybeGetPatron, function(patronid){\n var modalInstance = $uibModal.open({\n backdrop: false,\n templateUrl: '/app/static/partials/placehold-modal.html',\n controller: 'PlaceHoldsDlgCtrl',\n windowClass: \"modal placehold\",\n size: 'lg',\n resolve: {\n bibids: function() { return bibids; },\n patronid: function () { return patronid; }\n }\n });\n modalInstance.result.finally(function(){\n if(bvSearchToPick.patronid){\n bvSearchToPick.returnToOrigin();\n $rootScope.$broadcast('loadingResolve'); // #177004886\n }\n });\n return modalInstance.result;\n }, function(fail){\n console.warn(fail);\n });\n });\n\n },\n browseShelf: function(item){\n\n var modalInstance = $uibModal.open({\n // backdrop: false,\n templateUrl: '/app/static/partials/shelfbrowse-modal.html',\n controllerAs: 'shelf',\n size: 'lg',\n controller: [\"startItem\", function(startItem){\n // any click should close the modal, except carousel nav.\n console.log(startItem);\n this.closeOnSelect = function(modalClose, event){\n if( $(event.target).parents('div.carousel-inner').length){\n modalClose();\n }\n };\n\n this.startItem = startItem;\n this.branch = startItem.homebranch;\n this.cn = startItem.itemcallnumber;\n this.location = startItem.location;\n }],\n windowClass: \"shelfbrowse-viewer\",\n resolve: {\n startItem: function () {\n console.log(item);\n if(angular.isObject(item))\n return item;\n else\n return kwApi.Item.get({ id: item }).$promise;\n }\n }\n });\n return modalInstance.result;\n\n },\n delBibs: function( bibids ) {\n var modalInstance = $uibModal.open({\n backdrop: false,\n templateUrl: '/app/static/partials/del-bibs-modal.html',\n controller: 'DelBibsDlgCtrl',\n resolve: {\n bibids: function () {\n return bibids;\n }\n }\n });\n return modalInstance.result;\n },\n savedSearch: function () {\n $uibModal.open({\n backdrop: false,\n templateUrl: '/app/static/partials/savedSearch-modal.html',\n controller: 'SavedSearchCtrl'\n });\n return false;\n },\n lists: function (shelfid) {\n $uibModal.open({\n backdrop: false,\n templateUrl: '/app/static/partials/list-modal.html',\n size: 'lg',\n controller: 'ListDlgCtrl',\n windowClass: \"modal list-viewer\",\n resolve: {\n shelfnumber: function () {\n return shelfid;\n }\n }\n });\n return false;\n },\n addToList: function (bibids) {\n if(!userService.loggedin && configService.external_auth.saml && !configService.external_auth.saml.iframe){\n window.location.assign('/api/saml/login');\n return;\n }\n return userService.whenAuthenticatedUserDetails().then(function(d){\n var modalInstance = $uibModal.open({\n backdrop: false,\n templateUrl: '/app/static/partials/addtolist-modal.html',\n controller: 'AddToListDlgCtrl',\n windowClass: \"modal list-adder\",\n resolve: {\n bibids: function () {\n return bibids;\n }\n }\n });\n return modalInstance.result;\n });\n\n },\n lostPass: function () {\n // FIXME: debounce.\n $uibModal.open({\n backdrop: false,\n templateUrl: '/app/static/partials/lostpass-modal.html',\n controller: 'LostPassDlgCtrl',\n });\n\n return false;\n },\n\n recordView: function(bibid, view){\n var modalInstance = $uibModal.open({\n backdrop: true,\n templateUrl: '/app/static/partials/marcview-modal.html',\n size: 'lg',\n controller: [\"$scope\", \"$uibModalInstance\", function ($scope, $uibModalInstance) {\n $scope.type = (view == 'labeled') ? 'html' : 'text';\n $scope.closeModal = $uibModalInstance.close;\n if (view == 'labeled') {\n $http.get('/api/work/' + bibid + '/exports/html').\n then(function (response) {\n $scope.marc = response.data;\n $scope.kind = 'Expanded MARC';\n });\n } else if (view == 'isbd') {\n $http.get('/api/work/' + bibid + '/exports/isbd').\n then(function (response) {\n $scope.marc = response.data;\n $scope.kind = 'ISBD';\n });\n } else if (view == 'cite-apa') {\n $http.get('/api/work/' + bibid + '/cite?style=APA&format=html').\n then(function (response) {\n if (response.data.cite === null) {\n $scope.marc = 'Citation unavailable';\n } else {\n $scope.marc = response.data.cite;\n }\n $scope.kind = 'APA Citation';\n });\n } else if (view == 'solr') {\n $scope.closeModal = function(){\n $uibModalInstance.close($scope.index_time);\n };\n $http.get('/api/work/' + bibid + '/exports/solr').\n then(function (response) {\n $scope.marc = response.data;\n $scope.kind = 'Search Index';\n $scope.bibid = bibid;\n });\n $http.get('/api/work/' + bibid + '?view=index_info').\n then(function (response) {\n var last_changelog_entry = response.data.last_changelog_entry;\n var last_solr_index = response.data.last_solr_index;\n $scope.update_time = last_changelog_entry;\n $scope.index_time = last_solr_index;\n var updatetime = new Date(last_changelog_entry);\n var indextime = new Date(last_solr_index);\n if ( (indextime.getTime() - updatetime.getTime()) < 0 ) {\n $scope.stale = 1;\n }\n else {\n $scope.stale = 0;\n }\n });\n } else {\n $http.get('/api/work/' + bibid + '/exports/text').\n then(function (response) {\n $scope.marc = response.data;\n $scope.kind = 'MARC';\n });\n }\n\n $scope.indexNow = function (bibid) {\n $http.post('/api/work/' + bibid + '?op=reindex', {authRequired: true},\n {headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}}).\n then(function (response) {\n $scope.update = 1;\n $scope.stale = 0;\n $scope.index_status = '';\n }, function (response) {\n $scope.update = 0;\n if ( angular.isObject(response) ) {\n $scope.index_status = 'ERROR: ' + Object.keys(response);\n }\n else {\n $scope.index_status = 'ERROR: ' + (response || 'system error') ;\n }\n });\n $timeout(function () {\n $http.get('/api/work/' + bibid + '?view=index_info').\n then(function (response) {\n var last_changelog_entry = response.data.last_changelog_entry;\n var last_solr_index = response.data.last_solr_index;\n $scope.update_time = last_changelog_entry;\n $scope.index_time = last_solr_index;\n });\n $http.get('/api/work/' + bibid + '/exports/solr').\n then(function (response) {\n $scope.marc = response.data;\n $scope.kind = 'Search Index';\n $scope.bibid = bibid;\n });\n }, 2200);\n };\n\n }],\n windowClass: \"modal marc-viewer\",\n });\n return modalInstance.result;\n },\n hotkeyModal: function(){\n var modalInstance = $uibModal.open({\n templateUrl: '/app/static/partials/hotkeyModal.html',\n controller: [\"$scope\", \"$uibModalInstance\", \"kwHotkeySvc\", function($scope, $uibModalInstance, kwHotkeySvc){\n $scope.keys = kwHotkeySvc.list();\n\n var contexts = {\n global: { name: 'Global', actions: [] },\n marced: { name: 'Cataloging Editor', actions: []}\n };\n $scope.contexts = ($state.includes('staff.marced')) ?\n [ contexts.marced, contexts.global ] : [ contexts.global, contexts.marced ];\n\n var hotkey_actions = kwHotkeySvc.list();\n var active_hotkeys = kwHotkeySvc.list({active: true});\n for(var action in hotkey_actions){\n var context = hotkey_actions[action].context ? 'marced' : 'global';\n contexts[context].actions.push(action);\n $scope.keys[action] = {\n key: hotkey_actions[action].key,\n action: action,\n name: hotkey_actions[action].name,\n desc: hotkey_actions[action].desc,\n active: kwHotkeySvc.keyAction(hotkey_actions[action].key)==action\n };\n }\n // marced copy-paste.\n var cpModifier = (window.navigator.platform.match(/^Mac/)) ? 'cmd' : 'ctrl';\n $scope.keys.marcCopy = {\n key: cpModifier + '+c',\n name: 'Copy',\n desc: 'Copy MARC field or subfield',\n active: $state.includes('staff.marced')\n };\n $scope.keys.marcPaste = {\n key: cpModifier + '+v',\n name: 'Paste',\n desc: 'Paste MARC field or subfield',\n active: $state.includes('staff.marced')\n };\n contexts[context].actions.push('marcCopy');\n contexts[context].actions.push('marcPaste');\n\n }]\n });\n return modalInstance.result;\n },\n dialog: function(data){\n // simple generic dialog.\n // confirm, dialog, alert or wait .\n // confirm & alert just provide buttons for you.\n // options:\n // heading: string (required)\n // message: string|obj (required) [if obj: { tmpl: '/path/to/partial' } / uses ng-include]\n // type: string, 'dialog', 'notify', or 'wait'. these provide defaults for the following options:\n // alertClass: classname(s) for heading.\n // icon: icon classnames.\n // buttons: array:\n // [ {val: any, btnClass: classname, label: 'Ok'}, ... ]\n var modal = $uibModal.open({\n templateUrl: '/app/static/partials/dialogModal.html',\n controller: [\"dlgData\", \"$scope\", function(dlgData, $scope){\n // type: 'dialog', 'notify' or 'help'.\n // options :\n // icon: icon classnames in modal header\n // alertClass: heading classname\n // heading: text on modal header\n // message: text for body of message.\n // templateUrl: template to include in modal body.\n // close: bool -- show/hide close 'x'\n\n // You may prompt for user input (simple text inputs)\n // by including inputs array:\n // [ { name: str, label: str, val: defaultVal, type: str }, ...] .\n // options: an ngOptions comprehension_expression, making the input a SELECT.\n // (excluding options means its an input element with type==type||'text'.)\n\n // you may pass in scope vars for any comprehension expressions in a 'scopevars' prop.\n // -- take care not to clobber anything.\n\n // Buttons in that case should return BOOL to either pass through the input values\n // or return false.\n\n // Reusable dialog for confirmation / notification.\n\n //$scope.dlg = dlgData;\n // Defaults:\n // alertClass : error, info, warning, success .\n var defaults = {\n dialog: {\n icon: 'bi bi-check',\n message: 'Please confirm before proceeding.',\n heading: 'Are you sure?',\n buttons: [ { label: 'Yes', val: true, btnClass: 'btn-primary', submit: true },\n {label: 'No', val: false, btnClass: 'btn-outline-secondary'} ],\n alertClass: 'info'\n },\n notify: {\n icon: 'bi bi-info-sign',\n message: '',\n heading: 'Notice',\n buttons: [ { label: 'Ok', val: true} ],\n alertClass: 'info'\n },\n help: {\n icon: 'bi bi-question-square',\n heading: 'Help', alertClass: 'info',\n buttons: [ { label: 'Close', btnClass: 'btn-primary'}]\n }\n };\n\n if(!defaults[dlgData.type]) dlgData.type = 'dialog';\n\n $scope.dlg = angular.extend(defaults[dlgData.type], dlgData);\n $scope.modalBodyClass= dlgData.type + '-modal';\n for(var s in dlgData.scopevars){\n $scope[s] = dlgData.scopevars[s];\n }\n if(dlgData.ngInclude){\n $scope.templateUrl = dlgData.ngInclude;\n }\n if($scope.dlg.buttons)\n $scope.dlg.buttons.forEach(function(btnDef){\n if(!btnDef.btnClass) btnDef.btnClass = 'btn-outline-secondary' });\n if(dlgData.inputs){\n $scope.dlg.inputs = dlgData.inputs;\n $scope.dlg.input_params = function(){\n var ret = {};\n for (var i = 0; i < $scope.dlg.inputs.length; i++) {\n ret[$scope.dlg.inputs[i].name] = $scope.dlg.inputs[i].val;\n }\n return ret;\n\n };\n }\n if(!dlgData.icon ){\n if(dlgData.alertClass=='info'){\n $scope.dlg.icon = 'icon icon-info-sign';\n } else if(dlgData.alertClass=='danger' || dlgData.alertClass=='warning'){\n $scope.dlg.icon = 'icon icon-warning-sign';\n } else if(dlgData.alertClass=='success'){\n $scope.dlg.icon = 'icon icon-check';\n }\n }\n\n }],\n resolve: { dlgData: function(){\n return data;\n }},\n windowClass: 'dlg ' + data.alertClass\n });\n return modal;\n }\n };\n return svc;\n\n}]);\n\nmodule.factory('messageService', [\"$http\", \"$timeout\", \"$q\", \"userService\", \"alertService\", \"$rootScope\", \"$sce\", function($http, $timeout, $q, userService, alertService, $rootScope, $sce){\n\n var svc = {\n pullInterval: 300000,\n retryInterval: 5000, // No userid\n failInterval: 30000, // Server error\n running: 0,\n list: [],\n prefs: [],\n lastPollTime: new Date(),\n messageIdCache: {}\n };\n\n // FIXME - if we refresh on the messaging settings page the interval returns to default\n console.log(\"Calling whenAnyUD from message service\");\n var setPollPref = function(){\n userService.whenAnyUserDetails().then(function() {\n if (userService.merged_prefs) {\n if ('message_poll_interval' in userService.merged_prefs) {\n svc.setPollInterval(userService.merged_prefs.message_poll_interval);\n }\n }\n });\n };\n setPollPref();\n\n $rootScope.$on('loggedin', function(e, args) {\n console.log(\"Clearing message ID cache\");\n svc.messageIdCache = {};\n svc.lastPollTime = new Date();\n setPollPref();\n });\n\n svc.start = function() {\n if (svc.pullInterval > 0) {\n svc.running = 1;\n svc._pull(100);\n }\n };\n\n svc.stop = function() {\n svc.running = 0;\n };\n\n // Exposed to the outside world as \"poll\", not \"pull\", as the mechanism isn't relevant to the caller\n svc.getPollInterval = function() {\n var n = Math.floor(svc.pullInterval / 1000);\n return n;\n };\n\n svc.setPollInterval = function(n) {\n svc.pullInterval = Math.floor(n)*1000;\n svc.failInterval = svc.pullInterval + 10000; // entirely arbitrary\n if (n == 0) {\n if (svc.running) {\n svc.stop();\n }\n }\n else { \n if (!svc.running) {\n svc.start();\n }\n }\n };\n\n svc.getPrefs = function() {\n var deferred = $q.defer();\n var id = userService.id;\n $http.get('/api/patron/' + userService.id + '/message_preferences').then(function(rsp) {\n svc.prefs = rsp.data;\n deferred.resolve(svc.prefs);\n }, function() {\n deferred.reject();\n });\n return deferred.promise;\n };\n\n svc.updatePrefs = function() {\n var deferred = $q.defer();\n $http.put('/api/patron/' + userService.id + '/message_preferences', JSON.stringify(this.prefs),{authRequired: true}).then(function(rsp) {\n deferred.resolve(rsp.data);\n }, function() {\n deferred.reject();\n });\n\n var p2 = userService.addPrefs({message_poll_interval: Math.floor(svc.pullInterval/1000)});\n return $q.all([deferred.promise, p2]);\n };\n\n\n svc.getList = function() {\n var deferred = $q.defer();\n // Ensure we have a current list even if pull interval is large\n svc._getPending().then(function() {\n // Return a copy so we're not updating the DOM any time we refresh.\n deferred.resolve(angular.copy(svc.list));\n }, function() {\n deferred.reject();\n });\n return deferred.promise;\n };\n\n svc.clear = function(n) {\n var deferred = $q.defer();\n n.delivery_status = 'sent';\n $http.put('/api/message/' + n.id, JSON.stringify(n)).then(function() {\n deferred.resolve(1);\n }, function() {\n deferred.reject();\n });\n return deferred.promise;\n };\n\n\n svc._pull = function(delay) {\n if (svc.running) {\n $timeout(function() {\n svc._getPending().then(function(newDelay) {\n svc._pull(newDelay);\n }, function(newDelay) {\n svc._pull(newDelay)\n });\n }, delay);\n }\n };\n\n svc.get = function(id) {\n var deferred = $q.defer();\n $http.get('/api/message/' + id + '?format=json').then(function(rsp) {\n deferred.resolve(rsp.data);\n }, function(err) {\n deferred.reject(err);\n });\n return deferred.promise;\n };\n\n svc._getPending = function() {\n var deferred = $q.defer();\n var id = userService.id;\n if (id > 0) {\n var curTime = new Date();\n var elapsed = Math.floor((curTime - svc.lastPollTime) / 1000 + 60);\n\n // Don't keep anything in the cache more than an hour hold\n angular.forEach(svc.messageIdCache,function(val, key) {\n var age = (curTime - val)/1000;\n if (age > elapsed + 3600) {\n delete svc.messageIdCache[key];\n }\n });\n\n $http.get('/api/patron/' + id + '/messages?within=' + elapsed + '&message_endpoint_types=popup%2Clist',\n {headers:{'X-KohaOX-IdleReset':false}}).then(function(rsp) {\n svc.lastPollTime = curTime; // time of request, not response\n\n svc.list.length = 0;\n \n rsp.data.forEach(function(rec) {\n var n = rec.message;\n\n if (n.message_endpoint_type == 'popup' && n.code == 'sys.signal' && !(n.id in svc.messageIdCache)) {\n var content = JSON.parse(n.content);\n svc.messageIdCache[n.id] = curTime;\n \n $rootScope.$broadcast(\"sys.signal.\" + content['event'], content.parameters);\n n.delivery_status = 'sent';\n $http.put('/api/message/' + n.id, JSON.stringify(n));\n }\n else if (n.message_endpoint_type == 'popup' && !(n.id in svc.messageIdCache)) {\n svc.messageIdCache[n.id] = curTime;\n\n var msg = {};\n if (n.level && n.level > 1) {\n msg.type = 'error';\n msg.persist = true;\n }\n else {\n msg.type = 'info';\n }\n\n if (n.title) {\n msg.msg = '' + n.title + '
' + n.content;\n }\n else {\n msg.msg = n.content;\n }\n \n alertService.add(msg);\n n.delivery_status = 'sent';\n $http.put('/api/message/' + n.id, JSON.stringify(n));\n } else if (n.message_endpoint_type == 'list') {\n $sce.trustAsHtml(n.content);\n svc.list.push(n);\n }\n });\n deferred.resolve(svc.pullInterval);\n }, function(data) {\n deferred.reject(svc.failInterval);\n });\n }\n else {\n deferred.reject(svc.retryInterval);\n }\n return deferred.promise;\n };\n return svc;\n}]);\n\nmodule.factory('jobService', [\"$http\", \"$q\", \"userService\", function($http, $q, userService){\n\n var svc = {\n list: [],\n };\n\n svc.getList = function() {\n var deferred = $q.defer();\n var id = userService.id;\n if (id > 0) {\n $http.get('/api/patron/' + id + '/async-jobs').then(function(rsp) {\n svc.list.length = 0;\n rsp.data.forEach(function(rec) {\n svc.list.push(rec.job);\n });\n deferred.resolve(svc.list);\n }, function(e) {\n deferred.reject(e);\n });\n }\n else {\n deferred.reject(\"No user\");\n }\n return deferred.promise;\n };\n\n svc.remove = function(n) {\n var deferred = $q.defer();\n $http.delete('/api/async-job/' + n.id).then(function() {\n deferred.resolve(1);\n }, function() {\n deferred.reject();\n svc.reload(n);\n });\n return deferred.promise;\n };\n\n svc.suspend = function(n) {\n var deferred = $q.defer();\n $http.post(\"/api/async-job/\" + n.id, $.param({op: 'suspend'}),{headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}}).then(function() {\n deferred.resolve(1);\n }, function() {\n deferred.reject();\n svc.reload(n);\n });\n return deferred.promise;\n };\n\n svc.resume = function(n) {\n var deferred = $q.defer();\n $http.post(\"/api/async-job/\" + n.id, $.param({op: 'resume'}),{headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}}).then(function() {\n deferred.resolve(1);\n }, function() {\n deferred.reject();\n svc.reload(n);\n });\n return deferred.promise;\n };\n\n\n svc.update = function(n) {\n var deferred = $q.defer();\n $http.put('/api/async-job/' + n.id, JSON.stringify(n)).then(function() {\n deferred.resolve(1);\n }, function() {\n deferred.reject();\n svc.reload(n);\n });\n return deferred.promise;\n };\n\n svc.get = function(id) {\n var deferred = $q.defer();\n $http.get('/api/async-job/' + id).then(function(rsp) {\n deferred.resolve(rsp.data);\n }, function(err) {\n deferred.reject(err);\n });\n return deferred.promise;\n };\n\n svc.reload = function(n) {\n if (n.id) {\n $http.get('/api/async-job/' + n.id).then(function(rsp) {\n angular.extend(n, rsp.data);\n });\n }\n\n };\n\n return svc;\n}]);\n\n// TODO - could be moved to staff\nmodule.factory('importBatchService', [\"$http\", \"$q\", \"userService\", function($http, $q, userService) {\n\n var svc = {\n list: [],\n total_count: 0,\n start: 0,\n count: 20,\n sort: {\n field: 'upload_timestamp',\n reverse: true,\n }\n };\n\n svc.getList = function() {\n var deferred = $q.defer();\n var id = userService.id;\n if (id > 0) {\n $http.get('/api/import-batch'\n + '?view=list'\n + '&start=' + svc.start\n + '&count=' + svc.count\n + '&sort=' + encodeURIComponent(svc.sort.field)\n + '&dir=' + (svc.sort.reverse ? 'DESC' : 'ASC')\n ).then(function(resp) {\n svc.total_count = resp.data.total_count;\n svc.list.length = 0;\n resp.data.data.forEach(function(rec) {\n svc.list.push(rec);\n });\n\n svc.pager = new KOHA.Pager({numResults: resp.data.total_count, offset: 0});\n deferred.resolve(svc.list);\n }, function(e) {\n deferred.reject(e);\n });\n }\n else {\n deferred.reject(\"No user\");\n }\n return deferred.promise;\n };\n\n svc.toPage = function(page) {\n svc.start = (page-1) * svc.count;\n svc.getList();\n };\n\n svc.filter = function(batch, ids, async) {\n var deferred = $q.defer();\n \n $http.post(\"/api/import-batch/\" + batch.import_batch_id,\n $.param({\n op: 'filter',\n filter_ids: ids.join(','),\n async: async\n }),\n {headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}}\n ).then(function() {\n deferred.resolve(1);\n }, function(err) {\n deferred.reject(err);\n });\n return deferred.promise;\n };\n\n\n/* svc.remove = function(n) {\n var deferred = $q.defer();\n $http.delete('/api/async-job/' + n.id).then(function() {\n deferred.resolve(1);\n }, function() {\n deferred.reject();\n svc.reload(n);\n });\n return deferred.promise;\n };\n\n svc.suspend = function(n) {\n var deferred = $q.defer();\n $http.post(\"/api/async-job/\" + n.id, $.param({op: 'suspend'}),{headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}}).then(function() {\n deferred.resolve(1);\n }, function() {\n deferred.reject();\n svc.reload(n);\n });\n return deferred.promise;\n };\n\n svc.resume = function(n) {\n var deferred = $q.defer();\n $http.post(\"/api/async-job/\" + n.id, $.param({op: 'resume'}),{headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}}).then(function() {\n deferred.resolve(1);\n }, function() {\n deferred.reject();\n svc.reload(n);\n });\n return deferred.promise;\n };\n\n\n svc.update = function(n) {\n var deferred = $q.defer();\n $http.put('/api/async-job/' + n.id, JSON.stringify(n)).then(function() {\n deferred.resolve(1);\n }, function() {\n deferred.reject();\n svc.reload(n);\n });\n return deferred.promise;\n };\n\n svc.get = function(id) {\n var deferred = $q.defer();\n $http.get('/api/async-job/' + id).then(function(rsp) {\n deferred.resolve(rsp.data);\n }, function(err) {\n deferred.reject(err);\n });\n return deferred.promise;\n };\n\n svc.reload = function(n) {\n if (id) {\n $http.get('/api/async-job/' + id).then(function(rsp) {\n angular.extend(n, rsp.data);\n });\n }\n\n };*/\n\n return svc;\n}]);\n\n// TODO - could be moved to staff\nmodule.factory('bertService', [\"$http\", \"$q\", \"userService\", function($http, $q, userService) {\n\n var svc = {\n list: [],\n };\n\n svc.getList = function() {\n var deferred = $q.defer();\n var id = userService.id;\n if (id > 0) {\n $http.get('/api/bert?view=list').then(function(resp) {\n svc.list.length = 0;\n resp.data.forEach(function(rec) {\n svc.list.push(rec);\n });\n deferred.resolve(svc.list);\n }, function(data) {\n deferred.reject(data);\n });\n }\n else {\n deferred.reject(\"No user\");\n }\n return deferred.promise;\n };\n\n return svc;\n}]);\n\nmodule.factory('Pager', function(){\n // Pager class to track ranges.\n // Fits with uib's pagination, which updates 'page' via ng-model.\n return function(p){\n if(!('count' in p)) throw new Error(\"Pager requires count\");\n this.count = p.count;\n this.pagelength = p.pagelength || 20;\n this.page = p.page || 1;\n this.offset = function(){\n return this.pagelength * (this.page - 1);\n };\n this.rangeEnd = function(){\n return Math.min( this.count, this.offset() + this.pagelength);\n };\n this.numPages = function(){\n return Math.ceil(this.count||0 / this.pagelength);\n };\n };\n});\n\nmodule.factory('SearchQuery', [\"userService\", \"$state\", \"configService\", \"$q\", \"$http\", \"bibService\", \"SearchResults\", \"kwLuceneParser\", function(userService, $state, configService,\n $q, $http, bibService, SearchResults, kwLuceneParser){\n\n\n function SearchQuery(param){\n // query can be a string, as 'field:query'.\n // limits is an object of { field:value(s)}.\n // for now, all multivalued fields are assumed to be OR'ed.\n\n if(!param) param = {};\n if(param.fromState){\n // fromState may be $transition$ obj.\n var sparams = ( param.fromState.params ) ? param.fromState.params() : $state.params;\n\n this.q = sparams.query || '*:*';\n this.limitfields = {};\n for( var p in sparams ){\n if( (p=='fq' || configService.searchFields[p]) && sparams[p] !== undefined ){\n this.limitfields[p] = angular.copy(sparams[p]);\n }\n }\n param.page = sparams.page;\n param.sort = sparams.sort;\n if(sparams.op) param.op = sparams.op;\n\n } else if(typeof param.stateParams == \"function\" ){ // i.e. another SearchQuery\n this.q = param.q;\n this.sort = param.sort;\n this.page = param.page;\n this.limitfields = angular.copy(param.limitfields);\n } else {\n this.q = param.q || '*:*';\n this.limitfields = angular.copy(param.limits) || {}; // limitfields can hold 'fq' keyed values which are passed as is.\n }\n\n this.staticlimits = {}; // permissions-dependent, cannot be changed by user.\n this.sort = param.sort || null;\n this.page = param.page || 1;\n this.numPerPage = param.numPerPage || configService.OPACnumSearchResults;\n this.status = { error: false, searching: false, done: false };\n this.op = param.op; // Note server-side default is AND.\n\n // lost, deleted && suppression\n if(userService.is_staff){\n if(!this.limitfields.suppress || this.limitfields.suppress=='0')\n this.staticlimits.suppress = '[0 TO 1]';\n this.staticlimits.lost = '*';\n } else {\n //for non staff users, we use hidelostitems syspref to decide if we want \n // to display a bib that only has lost items\n if(!KOHA.config.hidelostitems){\n this.staticlimits.lost = '*';\n }\n }\n\n // sort and page may be in limits, which just come from url params, so extract them.\n if('sort' in this.limitfields){\n this.sort = this.limitfields.sort;\n delete this.limitfields.sort;\n } else {\n if(!this.sort && configService.OPACdefaultSortField){\n this.sort = configService.OPACdefaultSortField + ' ' + (configService.OPACdefaultSortOrder || 'desc');\n } else if(this.q=='*:*' && (this.sort === 'score desc' || !this.sort)){\n this.sort = 'acqdate desc';\n }\n }\n if('page' in this.limitfields){\n this.page = this.limitfields.page;\n delete this.limitfields.page;\n }\n\n for(var field in this.limitfields){\n if(!$.isArray(this.limitfields[field])) this.limitfields[field] = [this.limitfields[field]];\n }\n\n this.stateParams = function(newLimits){\n\n var qParams = { query: this.q };\n for ( var param in this.limitfields ) {\n if( this.limitfields[param] !== undefined )\n qParams[param] = this.limitfields[param];\n }\n if(this.op) qParams.op = this.op;\n if(this.page != 1)\n qParams.page = this.page;\n if(this.q == '*:*' && (this.sort === 'score desc' || !this.sort ))\n qParams.sort = 'acqdate desc';\n else\n qParams.sort = this.sort;\n\n return qParams;\n\n };\n\n this.asUrl = function() {\n return $state.href( 'search-results.koha', this.stateParams(), { inherit: false });\n }\n\n this.addLimit = function( fieldOrObj, value ){\n // addLimit( { itemtype: 'BOOK' }) or addLimit( 'itemtype', 'BOOK' );\n\n var newLimits = {};\n if( typeof fieldOrObj == 'object' ){\n angular.copy( fieldOrObj, newLimits );\n } else {\n newLimits[fieldOrObj] = value;\n }\n\n angular.forEach( newLimits, function( v, f ){\n if( v != '' ){\n // Note multiple limit values will simply be passed as repeated query parameters.\n var values = (Array.isArray(value) ? angular.copy(value) : [ v ]);\n if(!this.limitfields[f]) this.limitfields[f] = [];\n values.forEach( function(val){\n if(this.limitfields[f].indexOf(val)==-1) this.limitfields[f].push(val);\n }.bind(this));\n }\n }.bind(this));\n\n return this;\n };\n\n this.rmLimit = function(field, value){\n // remove a limit. pass value===null to remove all limits on that field.\n\n if(!jQuery.isArray(this.limitfields[field])) return;\n if(value===null){\n delete this.limitfields[field];\n } else {\n this.limitfields[field] = this.limitfields[field].filter(function(el){\n return (el != value) ? true : false ;\n });\n if(this.limitfields[field].length === 0){\n delete this.limitfields[field];\n }\n }\n return this;\n };\n this.hasLimits = function(){\n var keys = Object.keys(this.limitfields);\n // public users don't need to know about suppressed limits\n if (! userService.is_staff) {\n if (keys.length && keys.indexOf('suppress') > -1) {\n keys.splice(keys.indexOf('suppress'), 1);\n }\n }\n return (keys.length) ? true : false;\n };\n this.hasLimit = function(field, value){\n return this.limitfields[field] && this.limitfields[field].indexOf(value) != -1;\n };\n\n this.fq = function(){\n // get fq parameters as an array.\n var fqArr = [];\n //fixme: need to sort so that query string will be the same irrespective of fq order.\n jQuery.each(this.limitfields, function(field, limitArr){\n jQuery.each(limitArr, function(i, limitStr){\n\n if(field === 'fq'){\n fqArr.push( limitStr );\n } else {\n // limitStr is an array.\n\n if(limitStr.match(/^[(\\[]/) || field == 'geo-shape'){\n fqArr.push( field+\":\"+limitStr);\n } else {\n fqArr.push( field+\":(\"+limitStr+\")\");\n }\n }\n });\n });\n jQuery.each(this.staticlimits, function(field, limitStr){\n fqArr.push( field + ':' + limitStr );\n });\n if (userService.merged_prefs && userService.merged_prefs.default_query_filter) {\n if ((typeof(userService.merged_prefs.use_default_filter) != 'boolean') || userService.merged_prefs.use_default_filter) {\n fqArr.push(userService.merged_prefs.default_query_filter);\n }\n }\n // FIXME: should return [] ?\n return fqArr.length ? fqArr : null;\n };\n\n var defaultSolrParams = {\n 'facet.field': configService.SearchFacets\n };\n\n if(defaultSolrParams['facet.field'].indexOf('lexile_i') > -1) {\n defaultSolrParams['facet.range'] = 'lexile_i';\n defaultSolrParams['f.lexile_i.facet.range.start'] = 0;\n defaultSolrParams['f.lexile_i.facet.range.end'] = 2000;\n defaultSolrParams['f.lexile_i.facet.range.gap'] = 100;\n }\n\n var pubyear_intervals = {};\n if(defaultSolrParams['facet.field'].indexOf('pubyear')>-1){\n // TODO: Add range facets into config.\n var thisyear = new Date().getFullYear();\n var decadeThreshold = Math.floor( (thisyear - 4) / 10) * 10;\n var interval = 0;\n var pubdateIntervals = [];\n\n for (var y = thisyear; y > 1400 ; y-=interval) {\n if(y==thisyear) pubdateIntervals.push( '['+y+','+y+']');\n if(y > decadeThreshold){\n if(y-decadeThreshold >= interval*2+2 ){\n if(interval < 3) interval++;\n } else {\n if(y-decadeThreshold < interval*2) interval = y-decadeThreshold;\n }\n } else if(y > 1900){\n interval = 10;\n } else {\n interval = 100;\n }\n pubdateIntervals.push( '['+(y-interval)+','+y+')');\n }\n defaultSolrParams['f.pubyear.facet.sort'] ='index'; // this isn't working if pubyear_intervals isn't set.\n pubyear_intervals['facet.interval'] = 'pubyear';\n pubyear_intervals['f.pubyear.facet.interval.set'] = pubdateIntervals;\n }\n\n this.solrParams = function() {\n\n var self = this;\n var solrParams = angular.copy(defaultSolrParams);\n\n var lexidx = solrParams['facet.field'].indexOf('lexile_i');\n if (lexidx > -1) {\n solrParams['facet.field'].splice(lexidx, 1)\n }\n\n if((this.limitfields.pubyear||[''])[0].match(' TO ')){\n solrParams['f.pubyear.facet.limit'] = 100; // so we can sort reverse chrono.\n } else {\n angular.extend(solrParams, pubyear_intervals);\n }\n // FIXME: allow skipping facet requests.\n if(this.fq()){\n solrParams.fq = this.fq();\n if (userService.acl_fq) {\n solrParams.fq.push(userService.acl_fq);\n }\n }\n else if (userService.acl_fq) {\n solrParams.fq = [userService.acl_fq];\n }\n if(this.sort && this.sort != 'score desc'){\n solrParams.sort = this.sort;\n }\n if(this.op && this.op=='OR'){\n solrParams['q.op'] = this.op;\n solrParams.mm = 1; // if default mm is set server-side, q.op doesn't work.\n }\n var headers = {};\n\n var endrange = this.page * this.numPerPage - 1;\n var startrange = (this.page-1)*this.numPerPage;\n headers.Range = 'records='+ startrange +'-'+endrange;\n // Duplicate range data so the browser will cache, and some folks have misbehaving proxies.\n solrParams.start = startrange;\n solrParams.count = this.numPerPage;\n\n if(configService.OPACSearchSuggestionsCount){\n solrParams.spellcheck=true;\n solrParams['spellcheck.count'] = configService.OPACSearchSuggestionsCount * 2;\n solrParams['spellcheck.maxCollations'] = configService.OPACSearchSuggestionsCount * 2;\n solrParams['spellcheck.collate'] = true;\n }\n var embedResults = parseInt(configService.search.embeddedResults,10);\n if(!isNaN(embedResults)) solrParams.embed = embedResults;\n\n return {headers: headers, params: solrParams};\n };\n\n this.equals = function(otherSearch){\n if(otherSearch && otherSearch.stateParams){\n return angular.equals( this.stateParams(), otherSearch.stateParams());\n }\n };\n\n var resultsDeferred = $q.defer();\n this.whenResults = function(){\n return resultsDeferred.promise;\n };\n\n this.fetch = function(options){\n var opt = { facet: true };\n angular.extend(opt, options);\n\n // returns a promise resolving to a SearchResults.\n\n this.status.done = false;\n this.status.searching = true;\n\n var self = this;\n\n userService.setLastExecutedSearch([\n this.q,\n angular.copy(this.solrParams().params)\n ]);\n\n $http.get(\"/api/opac/\"+encodeURIComponent(this.q), this.solrParams())\n .then(function(rsp){\n rsp.data._resultsPerPage = self.numPerPage;\n self.results = new SearchResults(rsp.data, self);\n if(self.results.spellcheck.length > configService.OPACSearchSuggestionsCount){\n // FIXME: this isn't really appropriate here.\n self.results.spellcheck = self.results.spellcheck.slice(0,configService.OPACSearchSuggestionsCount);\n }\n self.status.error = false;\n self.status.searching = false;\n self.status.done = true;\n\n rsp.data.hits.forEach(function(result){\n if(result._embed && 'marc' in result._embed){\n bibService.put(result._embed);\n }\n });\n resultsDeferred.resolve(self.results);\n }, function(e){\n console.warn(e);\n self.status.error = true;\n self.status.done = true;\n self.status.searching = false;\n resultsDeferred.reject(e);\n });\n return resultsDeferred.promise;\n };\n this.testFQ = function(fqArr){\n return angular.equals(this.solrParams().params.fq, fqArr);\n };\n var _itemFacetConstraints;\n // this.makeItemConstraints = function() {\n this.itemFacetConstraints = function() {\n if(_itemFacetConstraints) return _itemFacetConstraints;\n var fq = this.solrParams().params.fq;\n if (fq === undefined) { return {} }\n\n var item_constraints = {};\n fq.forEach( function (s) {\n try {\n var key = s.split(':')[0];\n var value_list = s.split(':')[1].split('(')[1].split(')')[0];\n var values = value_list.split(' OR ');\n var r = /\"/g;\n values = values.map(function (s) {return s.replace(r, '')});\n item_constraints[key] = values;\n } catch (e) {\n ;\n }\n });\n _itemFacetConstraints = item_constraints;\n return _itemFacetConstraints;\n };\n this.moreFacets = function(field, start, count, sortby){\n if (!sortby) sortby = 'count';\n if(!count) count = 20;\n if(!start) start = 0;\n\n var solrParams = {\n 'facet.field' : field,\n 'facet.limit' : count,\n 'facet.sort' : sortby,\n 'facet.offset': start,\n rows : 0,\n };\n\n if(this.fq()){\n solrParams.fq = this.fq();\n if (userService.acl_fq) {\n solrParams.fq.push(userService.acl_fq);\n }\n }\n else if (userService.acl_fq) {\n solrParams.fq = [userService.acl_fq];\n }\n // TODO: Cache me.\n return $http.get(\"/api/opac/\"+encodeURIComponent(this.q), { \"params\" : solrParams })\n .then(function(rsp){\n var facet = rsp.data.facets[0];\n facet.values.forEach(function(facetval){\n if(this.hasLimit(facetval.field,facetval.value)) facetval.applied = true;\n }, this);\n return facet;\n }.bind(this), function(err){\n console.log(err);\n return $q.reject(err);\n });\n\n };\n this.go = function(){\n $state.go('search-results.koha', this.stateParams(), { inherit: false } );\n };\n this.toPage = function(pagenum){\n // call without param to use this search's results pager object.\n if(pagenum === undefined && (this.results||{}).pager) pagenum = this.results.pager.page;\n var newSearch = this.clone();\n newSearch.page = pagenum;\n newSearch.go();\n };\n\n this.reSort = function(sortby){\n var newSearch = this.clone();\n newSearch.sort = sortby;\n newSearch.go();\n };\n this.rawTerms = function(){\n return kwLuceneParser.extractTerms( kwLuceneParser.parse(this.q));\n }\n }\n SearchQuery.prototype.clone = function(){\n var clone = new SearchQuery(this);\n clone.page = 1;\n return clone;\n }\n\n return SearchQuery;\n}])\n.factory('SearchResults', function(){\n return function(solrData, searchQuery){\n this.bibs = solrData.hits.map(function(t){\n return { id: parseInt(t.work.substr(10),10),\n score: parseFloat(t.score).toFixed(3),\n title: (t._embed) ? null : t.title // supply title only if embedded bib isn't present.\n } ;\n });\n // FIXME: substr from uri is fragile way to get id.\n this.hits = solrData.total_hits;\n this.start = solrData.start;\n this.end = solrData.start + solrData.hits.length - 1;\n\n this.pager = new KOHA.Pager({numResults: this.hits, offset: this.start, numPerPage: solrData._resultsPerPage});\n this.facets = solrData.facets || {};\n this.spellcheck = solrData.spellcheck || [];\n this.has_facets = Object.keys(solrData.facets).length >=1;\n this.facets.forEach(function(facetField){\n facetField.values.forEach(function(facet){\n if(searchQuery.hasLimit(facet.field, facet.value)) facet.applied = true;\n });\n if(facetField.field=='pubyear' && !facetField.interval){\n facetField.values.reverse();\n }\n });\n this.selectedBibs = {};\n this.setSelected = function(bibid){\n if(bibid)\n this.selectedBibs[bibid] = true;\n else\n this.bibs.forEach(function(bib){ this.selectedBibs[bib.id] = true; }.bind(this));\n };\n this.clearSelected = function(bibid){\n if(bibid){\n delete this.selectedBibs[bibid];\n } else {\n Object.keys(this.selectedBibs).forEach(function(k){ delete this.selectedBibs[k]; }.bind(this));\n }\n };\n\n this.isFirst = function(bibid){\n return(this.pager && !this.pager.offset && this.bibs[0].id===parseInt(bibid,10));\n };\n this.isLast = function(bibid){\n return (this.isLastOnPage(bibid) && this.pager.page == this.pager.numPages);\n };\n this.isFirstOnPage = function(bibid){\n return(this.bibs[0].id===parseInt(bibid,10));\n };\n this.isLastOnPage = function(bibid){\n return this.bibs[this.bibs.length-1].id===parseInt(bibid,10);\n };\n this.step = function(bibid, dir){\n //returns next/previous bib in result set.\n var index;\n for(var i = 0; i < this.bibs.length; i++){\n if(parseInt(bibid,10) == this.bibs[i].id) index = i;\n }\n if(index===undefined || (index==this.bibs.length && dir>0) || (index===0 && dir<0)){\n return undefined;\n }\n return this.bibs[index+dir].id;\n\n };\n this.contains = function(bibid){\n // returns true if this results set contains bibid.\n for (var i = 0; i < this.bibs.length; i++) {\n if(this.bibs[i].id == bibid) return true;\n }\n return false;\n };\n\n };\n});\n\nmodule.factory('awLogService', [\"$uibModal\", function($uibModal){\n var svc = {};\n svc.startModal = function(){\n //ptfs\n var bb = $uibModal.open({\n templateUrl: '/app/static/partials/awLogView-modal.html',\n controller: 'AWLogViewerCtrl',\n windowClass: \"modal\",\n backdrop: 'static',\n resolve: {\n logID : null\n }\n });\n \n }\n \n return svc;\n }]);\n\nmodule.factory('awDailyExportService', [\"$uibModal\", function($uibModal){\n var svc = {};\n svc.startModal = function(q_str){\nconsole.log(q_str);\n var bb = $uibModal.open({\n templateUrl: '/app/static/partials/dailyExport-modal.html',\n controller: 'AWDailyExportCtrl',\n windowClass: \"modal dailyExportModalShell\",\n backdrop: 'static',\n resolve: {\n q_str: function () {\n return q_str;\n }\n }\n });\n \n }\n \n return svc;\n }]);\n\nmodule.provider('modalState', [\"$stateProvider\", function($stateProvider) {\n var provider = this;\n this.$get = function() {\n return provider;\n }\n this.state = function(stateName, options) {\n var modalInstance;\n $stateProvider.state(stateName, {\n url: options.url,\n onEnter: [\"$uibModal\", \"$state\", function($uibModal, $state) {\n modalInstance = $uibModal.open(options);\n modalInstance.result['finally'](function() {\n modalInstance = null;\n if ($state.$current.name === stateName) {\n // FIXME - what to do about nested modals? See app.js run section\n $state.go($state.previous);\n }\n });\n }],\n onExit: function() {\n if (modalInstance) {\n modalInstance.close();\n }\n }\n });\n }\n}]);\n\nmodule.factory('marcTemplateService', [\"$http\", \"$q\", \"userService\", function($http, $q, userService) {\n\n var svc = {\n list: [],\n };\n\n svc.getList = function() {\n var deferred = $q.defer();\n var id = userService.id;\n if (id > 0) {\n $http.get('/api/marctemplate/').then(function(resp) {\n svc.list.length = 0;\n resp.data.forEach(function(rec) {\n svc.list.push(rec);\n });\n deferred.resolve(svc.list);\n }, function(data) {\n deferred.reject(data);\n });\n }\n else {\n deferred.reject(\"No user\");\n }\n return deferred.promise;\n };\n\n return svc;\n}]);\n\nmodule.service('modalForm', [\"$uibModal\", \"$q\", \"$http\", \"$templateCache\", function($uibModal, $q, $http, $templateCache) {\n this.open = function(formConfig) {\n var templatePromise;\n\n var saveText = formConfig.saveText || 'Save';\n var config = angular.extend({\n header: '
' +\n '' +\n '

{{ title }}

' +\n '
' + \n '
\\n',\n footer: '
\\n' +\n '
' +\n '' +\n '' + \n '
',\n// wrapper: '
'\n }, formConfig);\n \n var header = '', footer = '';\n if (config.wrapper) {\n header = config.wrapper;\n footer = '
';\n }\n\n header = header + config.header;\n footer = config.footer + footer;\n\n if (config.templateUrl) {\n templatePromise = $http.get(config.templateUrl, {cache: $templateCache})\n .then(function(response) {\n var s = header + response.data + footer;\n return s;\n });\n }\n else {\n templatePromise = header + config.template + footer;\n }\n\n var deferred = $q.defer();\n \n var modalInstance = $uibModal.open({\n template: templatePromise,\n dialogClass: 'xmodal',\n backdropClass: 'modal-backdrop',\n controller: [\"$scope\", \"model\", \"$uibModalInstance\", function($scope, model, $uibModalInstance) {\n $scope.title = model.title;\n $scope.message = model.message;\n $scope.buttons = model.buttons;\n $scope.formdata = model.formdata;\n\n if (model.link) {\n model.link($scope);\n }\n $scope.close = function(res){\n $uibModalInstance.close(res);\n };\n console.log(\"Controller built\");\n }],\n\n backdrop: false, //'static',\n backdropClick: false,\n scope: config.scope,\n resolve: {\n model: function() { return config; }\n },\n size: config.size\n });\n\n modalInstance.result.then(function(result) {\n if (!result) {\n deferred.reject(result);\n }\n else {\n deferred.resolve(result);\n }\n });\n\n return deferred.promise;\n };\n}]);\n\nmodule.service('kwNoveListSvc', [\"configService\", \"$q\", \"$http\", function(configService, $q, $http){\n\n if (!configService.NoveListConfig) return {};\n// http://novselect.ebscohost.com/Data/ContentByQuery?profile=some_profile_here&password=some_password_here\n// &ClientIdentifier=SomethingMeaningful&UPC=9780441008537&version=2.3\n\n var noveListApiHost = '//novselect.ebscohost.com/Data/ContentByQuery';\n var noveListParams = {\n version : '2.3',\n profile : configService.NoveListConfig.profile,\n password : configService.NoveListConfig.password\n };\n\n var contentTypes = this.contentTypes = {\n seriesTitles: {\n backlink: 'isbn',\n objpath: [ 'SeriesInfo', 'series_titles' ] // jsonpath map to novelist api data.\n },\n similarTitles: {\n backlink: 'isbn',\n objpath: [ 'SimilarTitles', 'titles' ]\n },\n similarSeries: {\n backlink: false,\n objpath: [ 'SimilarSeries', 'series' ]\n },\n goodReads: {\n backlink: false,\n objpath: [ 'GoodReads' ]\n },\n similarAuthors: {\n backlink: false,\n objpath: [ 'SimilarAuthors', 'authors' ]\n }, // we should probably filter by author search here.\n relatedContent: {\n backlink: false,\n objpath: [ 'RelatedContent', 'doc_types' ]\n },\n appeals: {\n objpath: ['Appeals']\n },\n lexile: {\n objpath: ['LexileInfo']\n }\n };\n\n// todo: search by appeals:\n//https://novselect.ebscohost.com/api/appeals/ByTerm?profile=s9035634.main.novsel&password=dGJyMOPY8VGxq7YA&appeal=Fast-paced&readingLevel=Teen&isbnToExclude=9780439023481&format=fiction&author=Collins,%20Suzanne&genres=Science%20fiction,Dystopian%20fiction,First%20person%20narratives,Books%20to%20movies,Books%20for%20reluctant%20readers,&callback=novSelect.appealsSearchClbk\n\n var kohaBibs = {}; // isbn lookup cache for backlinks to local catalog.\n var noveListContentByKohaIsbn = {}; // cache all novelist content.\n // TODO: limit cache size.\n\n var findPrimaryIsbnFromManifestation = function(kohaISBNs, novelistISBNs){\n for (var i = 0; i < kohaISBNs.length; i++) {\n for ( var primary_isbn in novelistISBNs ){\n if( novelistISBNs[primary_isbn].indexOf(kohaISBNs[i])>-1 ){\n // console.log('matched: ' + kohaISBNs[i] + ' -> ' + primary_isbn);\n return primary_isbn;\n }\n }\n }\n };\n\n\n var findBacklinks = function(novelistTitles){\n var novelistISBNs = {};\n var promises = [];\n\n (novelistTitles||[]).forEach(function(novTitle){\n novelistISBNs[novTitle.primary_isbn] =\n novTitle.manifestations.map(function(mani){ return mani.ISBN; });\n });\n\n var searchList = []; // {isbn,promise}\n // assume primary_isbn is included in manifestations array //\n\n var solrParams = { facet: false };\n var solrQuery = [];\n for ( var primary_isbn in novelistISBNs ){\n if(primary_isbn in kohaBibs){\n // already searched.\n searchList.push( { isbn: primary_isbn, promise: kohaBibs[primary_isbn].promise });\n continue;\n }\n kohaBibs[primary_isbn] = $q.defer();\n searchList.push( { isbn: primary_isbn, promise: kohaBibs[primary_isbn].promise });\n\n solrQuery.push( novelistISBNs[primary_isbn].reduce(function(qsofar, isbn, i){\n qsofar += 'isbn:\"' + isbn + '\"';\n if(isbn==primary_isbn) qsofar += '^2'; //boost\n if(i 1) ? 'Batch' : fetchlist[0];\n var params = { ISBN: fetchlist, ClientIdentifier: clientId };\n\n return $http.get( noveListApiHost, {\n params: angular.extend( params, noveListParams ),\n cache: true,\n }).then(function(rsp){\n var data;\n if(rsp.data.FeatureContent){\n data = [ rsp.data ];\n } else if(rsp.data.titles){ // FIXME: UNimplemented !\n data = rsp.data.titles; // .map(function(d){ return d.FeatureContent; });\n }\n data.forEach(function(novelistContent){\n processContent(novelistContent);\n } );\n\n } );\n };\n\n this.getContent = function(isbn, contentType){\n // returns promises for each content type,\n // or a hash of such promises if no contentType specified.\n\n if(!( isbn in noveListContentByKohaIsbn ))\n this.fetchIsbns(isbn);\n\n if(contentType){\n if ( this.contentTypes[contentType].backlink )\n return noveListContentByKohaIsbn[isbn][contentType].ensureBacklinked().promise;\n else\n return noveListContentByKohaIsbn[isbn][contentType].promise;\n } else {\n var allContent = {};\n for (var cType in this.contentTypes) {\n allContent[cType] = noveListContentByKohaIsbn[isbn][cType].promise;\n if(this.contentTypes[cType].backlink) noveListContentByKohaIsbn[isbn][cType].ensureBacklinked(); // this already happens\n }\n return allContent;\n }\n };\n}]);\n\n\nmodule.service('loading', [\"$rootScope\", \"$q\", \"alertService\", function($rootScope, $q, alertService) {\n //this.pendingCount = 1; // if we decide to start with a spinner and resolve in KohaCtrl...\n this.globalCount = 0;\n this.activeKeys = {};\n\n this.add = function(key) {\n if (key)\n this.activeKeys[key] = 1;\n else\n this.globalCount++;\n\n //$('#loading').show();\n //$rootScope.loading = true;\n // XXX why are broadcasting through the entire scope tree?\n $rootScope.$broadcast('loadingAdd');\n };\n\n\n this.resolve = function(key) {\n if (key)\n delete this.activeKeys[key];\n else\n this.globalCount--;\n\n if ((this.globalCount <= 0) && (Object.keys(this.activeKeys).length == 0)) {\n this.globalCount = 0;\n //$('#loading').hide();\n //$rootScope.loading = false;\n $rootScope.$broadcast('loadingResolve');\n\n }\n };\n\n this.wrap = function(p, s) {\n var svc = this;\n svc.add('apiwrap');\n return p.then(function(succ) {\n svc.resolve('apiwrap');\n return succ;\n }, function(err) {\n svc.resolve('apiwrap');\n alertService.addApiError(err,(s || 'Error'));\n return $q.reject(err);\n });\n };\n\n}]);\n\n\nmodule.service('Logger', [\"$http\", function ($http) { // See logClickEvent directive\n return {\n logEvent: function(url) {\n var errorCB = function(e) { console.error(e); };\n $http.get(url).then(undefined, errorCB);\n }\n };\n}]);\n\n// TODO: Roll whatever is missing from this into kwDlgSvc as this seems to duplicate functionality.\nmodule.service('messageBox', [\"$uibModal\", \"$q\", function($uibModal,$q) {\n this.YESNO = [{result: 1, label: 'Yes'}, {result: undefined, label: 'No'}];\n this.YESNOCANCEL = [{result: 1, label: 'Yes'}, {result: 0, label: 'No'}, {result: undefined, label: 'Cancel'}];\n\n this.open = function(config) {\n var modalInst = $uibModal.open({\n template: \n \"
\\n\" +\n \"

{{ title }}

\\n\" +\n \"
\\n\" +\n \"
\\n\" +\n \" {{ message }}\\n\" +\n \"
\\n\" +\n \"
\\n\" +\n \" {{ btn.label }}\\n\" +\n \"
\\n\" ,\n\n dialogClass: 'modal',\n backdropClass: 'modal-backdrop',\n controller: [\"$scope\", \"model\", \"$uibModalInstance\", function($scope, model, $uibModalInstance) {\n $scope.title = model.title;\n $scope.message = model.message;\n $scope.buttons = model.buttons;\n $scope.close = function(res){\n $uibModalInstance.close(res);\n };\n }],\n backdrop: 'static',\n backdropClick: false,\n resolve: {\n model: function() { return config; }\n }\n });\n //Log.ger.dir(modalInst);\n \n return modalInst.result;\n };\n\n // Returns a promise which is rejected if the user clicks a button with a undefined value.\n this.confirm = function(config) {\n config = angular.extend({}, {buttons: this.YESNO}, config);\n\n var deferred = $q.defer();\n this.open(config).then(function(result) {\n if (typeof(result) === 'undefined') {\n deferred.reject(result);\n }\n else {\n deferred.resolve(result);\n }\n });\n return deferred.promise;\n };\n\n // Convenient ways skip a confirmation in a promise chain\n this.resolve = function(val) {\n var deferred = $q.defer();\n deferred.resolve(val);\n return deferred.promise;\n };\n\n this.reject = function(val) {\n var deferred = $q.defer();\n deferred.reject(val);\n return deferred.promise;\n };\n}]);\n\nmodule.factory('kwFileUploadSvc', [\"Upload\", \"$q\", \"$uibModal\", \"$http\", \"alertService\", function( Upload, $q, $uibModal, $http, alertService) {\n var svc = {};\n\n svc.upload = function(config) {\n var hasFormdata = (config.formdata ? true : false);\n config = angular.extend({\n title: 'Upload File',\n description: 'Select a file to upload',\n formdata: {},\n inputs: [],\n fileRequired: true,\n uploadButtonText: 'Upload',\n }, config);\n\n if (hasFormdata) {\n angular.forEach(config.inputs, function(v) {\n v.value = config.formdata[v.name];\n });\n }\n\n \n var deferred = $q.defer();\n var modalInstance = $uibModal.open({\n templateUrl: '/app/static/partials/components/kw-file-upload-modal.html',\n dialogClass: 'xmodal',\n backdropClass: 'modal-backdrop',\n backdrop: false,\n backdropClick: false,\n resolve: {\n config: function() {\n return config;\n }\n },\n controller: [\"$scope\", \"config\", \"$uibModalInstance\", function($scope, config, $uibModalInstance) {\n $scope.config = config;\n $scope.errors = [];\n $scope.progress = {\n percent: 0,\n loaded: 0,\n total: 0\n };\n $scope.upload = function() {\n angular.forEach(config.inputs, function(v) {\n config.formdata[v.name] = v.value;\n });\n\n var postdata = {};\n if (config.json) {\n postdata.file = config.formdata.file;\n var encoded = {};\n angular.forEach(config.formdata, function(val, key) {\n if (key !== 'file') {\n encoded[key] = val;\n }\n });\n postdata.json = JSON.stringify(encoded);\n }\n else {\n postdata = config.formdata;\n }\n\n\n $scope.uploading = true;\n $scope.errors = [];\n\n var postPromise;\n if (postdata.file) {\n postPromise = Upload.upload({\n url: config.url,\n data: postdata\n });\n }\n else {\n postPromise = $http.post(config.url, $.param(postdata),{headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}});\n }\n\n postPromise.then(function(resp) {\n $scope.uploading = false;\n if (config.responseValidator) {\n var rv = config.responseValidator(resp);\n if (rv) {\n $uibModalInstance.close(resp);\n }\n }\n else {\n $uibModalInstance.close(resp);\n }\n }, function(err) {\n if (typeof(err) === 'object') {\n err = err.data || err.statusText\n }\n var matches = err.match(/Upload Error (\\[.+)/);\n if (matches && matches.length == 2) {\n var errs = JSON.parse(matches[1]);\n $scope.errors = errs;\n }\n else {\n alertService.add({msg: \"Upload failed: \" + err, type: \"error\"});\n }\n $scope.uploading = false;\n }, function(evt) {\n $scope.progress.loaded = evt.loaded;\n $scope.progress.total = evt.total;\n $scope.progress.percent = parseInt(100.0 * evt.loaded / evt.total);\n });\n };\n\n if (config.link) {\n config.link($scope);\n }\n }],\n\n size: config.size\n });\n\n modalInstance.result.then(function(result) {\n if (!result) {\n deferred.reject(result);\n }\n else {\n deferred.resolve(result);\n }\n });\n\n return deferred.promise;\n };\n return svc;\n}]);\nmodule.factory('kwImportFactorySvc', [\"$q\", \"kwApi\", \"loading\", \"alertService\", \"configService\", \"kwFileUploadSvc\", \"$uibModal\", function($q, kwApi, loading, alertService, configService, kwFileUploadSvc, $uibModal) {\n var svc = {};\n\n svc.executeScript = function(script, vendor, ledger) {\n if (!script.is_approved) {\n return $uibModal.open({\n templateUrl: '/app/static/partials/staff/tools/ledger-importer/script-unapproved.html',\n resolve: {\n tainted: [\"kwApi\", function(kwApi) {\n return kwApi.ImportScript.get({id: script.id}).$promise.then(function(rv) {\n var tainted = [];\n rv._embed.scriptlets.forEach(function(s) {\n if (s.tainted === 1 || s.tained === '1')\n tainted.push(s.id);\n });\n return tainted.join(' ');\n });\n }],\n },\n controller: [\"$scope\", \"tainted\", function($scope, tainted) {\n $scope.tainted = tainted;\n }],\n });\n }\n\n loading.add();\n \n var scriptsPromise = kwApi.ImportScript.get({id: script.id}).$promise;\n var vendorsPromise = kwApi.Vendor.getList().$promise;\n var ledgersPromise;\n if (vendor) {\n ledgersPromise = kwApi.Vendor.getLedgers({id: vendor}).$promise;\n }\n else {\n ledgersPromise = $q.when(null);\n }\n\n $q.all([scriptsPromise, vendorsPromise, ledgersPromise]).then(function(promises) {\n var data = promises[0];\n var vendors = [];\n angular.forEach(promises[1], function(v) {\n vendors.push({display: v.branchname, value: v.branchcode});\n });\n var ledgers = [];\n if (promises[2] !== null) {\n angular.forEach(promises[2], function(ledger) {\n ledgers.push({display: ledger.ledger_code, value: ledger.id});\n });\n }\n\n loading.resolve();\n var params = [];\n angular.forEach(data._embed.scriptlets, function(s) {\n angular.forEach(s.parameters, function(p) {\n p.value = s.embed_parameter_values[p.name];\n params.push(p);\n });\n });\n\n var isExport = (script.script_type.match('analysis') ? true : false);\n\n var description = script.script_type.match('marc') ? 'Choose a MARC batch to upload' :\n isExport ? 'You may optionally upload a batch of MARC records to be used as the starting point for analysis' :\n 'Choose an Excel spreadsheet to upload. If your data is not in the first tab, specify the tab below';\n\n var inputs = [];\n var selectConfig = {\n maxItems: 1,\n valueField: 'id',\n labelField: 'name',\n create: true,\n };\n\n inputs.push({name: 'async', type: 'select', label: 'Run Mode', value: 0, values: [\n {display: 'Synchronous (wait for completion)', value: 0},\n {display: 'Asynchronous (run in background)', value: 1},\n ]});\n\n if (configService.infomart) {\n inputs.push({name: 'vendor', type: 'select', label: 'Vendor', values: vendors, value: vendor});\n inputs.push({name: 'ledger', type: 'select', label: 'Ledger', values: ledgers, value: ledger});\n }\n inputs.push({name: 'parameter_set', type: 'selectize', config: selectConfig, label: 'Parameter Set', values: []});\n\n if (isExport) {\n inputs.push({name: 'export_as', type: 'text', label: 'Export as filename'});\n inputs.push({name: 'save_run_log', type: 'select', label: 'Save Run Log', value: 0, values: [\n {display: 'No', value: 0},\n {display: 'Yes', value: 1},\n ]});\n }\n else {\n inputs.push({name: 'batch_create', type: 'select', label: 'Create Batch', value: 0, values: [\n {display: 'No (test only)', value: 0},\n {display: 'Yes (ignore errors)', value: 1},\n {display: 'Yes (fail on errors)', value: 'strict'}]});\n if (script.script_type.match('sheet')) {\n inputs.push({name: 'sheet', type: 'text', label: 'Spreadsheet Tab'}); \n }\n inputs.push({name: 'bib_action', type: 'select', label: 'Bib Action', value: 'replace', values: [\n {display: 'From Leader 05', value: 'marc_leader'},\n {display: 'Replace', value: 'replace'},\n {display: 'Create New', value: 'create_new'},\n {display: 'Delete', value: 'delete'}\n ]});\n inputs.push({name: 'item_action', type: 'select', label: 'Item Action', value: 'replace', values: [\n {display: 'From 952$@', value: 'item_del_field'},\n {display: 'Replace', value: 'replace'},\n {display: 'Always Add', value: 'always_add'},\n {display: 'Delete', value: 'delete'},\n {display: 'Ignore', value: 'ignore'}\n ]});\n inputs.push({name: 'batch_comments', type: 'text', label: 'Batch Comments'});\n inputs.push({name: 'save_run_log', type: 'select', label: 'Save Run Log', value: 0, values: [\n {display: 'No', value: 0},\n {display: 'Yes', value: 1},\n ]});\n }\n\n angular.forEach(params, function(p) { inputs.push(p); });\n\n // Deal with script parameters\n\n kwFileUploadSvc.upload({\n title: 'Run Script: ' + script.name,\n description: description,\n instructions: true,\n inputs: inputs,\n fileRequired: !isExport,\n uploadButtonText: 'Run',\n json: true,\n url: '/api/import-script/' + script.id + '?op=execute',\n link: function(scope) {\n\n var vendor_id = (configService.infomart ? scope.config.inputs[1].value : null);\n var ledger_id = (configService.infomart ? scope.config.inputs[2].value : null);\n\n scope.reloadParameterSets = function() {\n kwApi.ImportScript.getParameters({\n id: script.id,\n vendor_id: vendor_id,\n ledger_id: ledger_id,\n }).$promise.then(function(rv) {\n if (configService.infomart)\n scope.config.inputs[3].values = rv;\n else\n scope.config.inputs[1].values = rv;\n });\n };\n scope.reloadParameterSets();\n\n scope.applyParameters = function(map) {\n var n = (configService.infomart ? 3 : 1);\n for (var i=n+1; iGo to your account.

\"\n });\n deferred.resolve(1);\n }, function(err) {\n deferred.reject(err);\n });\n }, 500);\n }, function(err) {\n if (typeof(err) === 'object')\n err = err.data;\n if (/not recognized or linked/.test(err)) {\n deferred.reject(\"You haven't linked your \" + type + \" account with your library account yet! Log in using your user name and password, then under your user menu go to Account Home, then Social Login. From there, you can link to social media accounts.\");\n }\n else {\n alertService.add({\n type: \"error\",\n msg: \"Unable to login via \" + type + \": \" + err\n });\n deferred.reject('');\n }\n });\n return deferred.promise;\n };\n\n svc.linkAccount = function(type) {\n kwApi.Login.linkSocialInit({type: type},{}).$promise.then(function(rv) {\n $window.location.href = rv.url;\n });\n };\n\n svc.linkFinalize = function(type, code) {\n var deferred = $q.defer();\n kwApi.Login.linkSocialFinalize({type: type, code: code},{}).$promise.then(function(rv) {\n deferred.resolve(rv);\n }, function(err) {\n if (typeof(err) === 'object')\n err = err.data;\n\n alertService.add({\n type: \"error\",\n msg: \"Unable to link to \" + type + \": \" + err\n });\n deferred.reject(err);\n });\n return deferred.promise;\n };\n return svc;\n}]);\n\n\nmodule.factory('kwHotkeySvc', [\"configService\", function(configService){\n\n var keymap = {};\n var action = {};\n\n // hotkeys are generally registered/deregistered in controllers or directives and stored here.\n // handlers are a stack, allowing overrides in different parts of the application.\n\n\n var defaultHotkeyMap = angular.copy(configService.hotkeyDefs);\n\n var specialKeys = {\n 8: \"backspace\", 9: \"tab\", 13: \"return\", 16: \"shift\", 17: \"ctrl\", 18: \"alt\", 19: \"pause\",\n 20: \"capslock\", 27: \"esc\", 32: \"space\", 33: \"pageup\", 34: \"pagedown\", 35: \"end\", 36: \"home\",\n 37: \"left\", 38: \"up\", 39: \"right\", 40: \"down\", 45: \"insert\", 46: \"del\", 59: \";\", 61: \"=\",\n 96: \"0\", 97: \"1\", 98: \"2\", 99: \"3\", 100: \"4\", 101: \"5\", 102: \"6\", 103: \"7\",\n 104: \"8\", 105: \"9\", 106: \"*\", 107: \"+\", 109: \"-\", 110: \".\", 111 : \"/\",\n 112: \"f1\", 113: \"f2\", 114: \"f3\", 115: \"f4\", 116: \"f5\", 117: \"f6\", 118: \"f7\", 119: \"f8\",\n 120: \"f9\", 121: \"f10\", 122: \"f11\", 123: \"f12\", 144: \"numlock\", 145: \"scroll\",\n 173: \"-\", 186: \";\", 187: \"=\", 188: \",\", 189: \"-\", 190: \".\", 191: \"/\", 192: \"`\",\n 219: \"[\", 220: \"\\\\\", 221: \"]\", 222: \"'\"\n };\n\n var shiftNums = {\n \"`\": \"~\", \"1\": \"!\", \"2\": \"@\", \"3\": \"#\", \"4\": \"$\", \"5\": \"%\", \"6\": \"^\", \"7\": \"&\",\n \"8\": \"*\", \"9\": \"(\", \"0\": \")\", \"-\": \"_\", \"=\": \"+\", \";\": \": \", \"'\": \"\\\"\", \",\": \"<\",\n \".\": \">\", \"/\": \"?\", \"\\\\\": \"|\"\n };\n var bareModifier = {\n 16: \"shift\", 17: \"ctrl\", 18: \"alt\", 91: 'meta', 92: 'meta'\n }\n function keyHandler ( event ) {\n\n // allow esc key to trigger blur() [if in input ].\n if($(event.target).is(\"input,textarea,select\")){\n if(event.keyCode== 27 ){\n $(event.target).blur();\n event.stopPropagation();\n event.preventDefault();\n }\n if(!( event.ctrlKey||event.altKey||event.metaKey ))\n return;\n }\n if(bareModifier[event.which])\n return;\n\n var special = specialKeys[ event.which ],\n character = String.fromCharCode( event.which ).toLowerCase(),\n modif = \"\", possible = {};\n\n // from : github.com/jeresig/jquery.hotkeys\n\n jQuery.each([\"alt\", \"ctrl\", \"shift\"], function(index, specialKey) {\n if (event[specialKey + 'Key'] && special !== specialKey)\n modif += specialKey + '+';\n });\n\n // metaKey is triggered off ctrlKey erronously\n if (event.metaKey && !event.ctrlKey && special !== \"meta\")\n modif += \"meta+\";\n\n if (event.metaKey && special !== \"meta\" && modif.indexOf(\"alt+ctrl+shift+\") > -1)\n modif = modif.replace(\"alt+ctrl+shift+\", \"hyper+\");\n\n if (special) {\n possible[modif + special] = true;\n } else if(shiftNums[character]){\n possible[modif + shiftNums[character]] = true;\n if (modif === \"shift+\") { // \"$\" can be triggered as \"Shift+4\" or \"Shift+$\" or just \"$\"\n possible[shiftNums[character]] = true;\n }\n } else {\n possible[modif + character] = true;\n }\n\n for( var keyspec in possible ){\n if(keymap[keyspec] && action[keymap[keyspec][0]]){\n // $timeout(function(){\n // console.log('FIRING ACTION FOR ' + keyspec );\n action[keymap[keyspec][0]].call( event, event );\n // });\n event.preventDefault(); // FIXME: should be optional.\n return false;\n }\n }\n }\n\n // bind here rather than in directive, since there's nothing extra to do.\n $(document).bind('keydown', keyHandler);\n\n return {\n\n registerKey : function(key, actionId){\n // add to hotkeymap.\n if(!key.length) throw new Error('No key');\n // console.log('Registering: ' + actionId + ' -> ' + key);\n if(!action[actionId]) throw new Error('No action');\n\n if(keymap[key]){\n var already = keymap[key].indexOf(actionId);\n if(already != -1){\n console.warn('Duplicate hotkey registration at index: ' + already);\n keymap[key].splice(already, 1);\n }\n keymap[key].unshift(actionId);\n } else {\n keymap[key] = [actionId];\n }\n\n },\n register : function(actionId, fn, key){\n // registers an action. uses default keys if not provided.\n try {\n if(typeof fn === 'function')\n action[actionId] = fn;\n\n if(!key) key = defaultHotkeyMap[actionId].key;\n this.registerKey(key, actionId);\n return key;\n } catch (e) {\n console.warn('Hotkey registration failed for: ' + actionId);\n }\n\n },\n\n deregister : function(actionId){\n // remove action and any triggering keys.\n\n delete action[actionId];\n for(var k in keymap){\n keymap[k] = keymap[k].filter(function(a){ return a != actionId; });\n if(!keymap[k].length) delete keymap[k];\n }\n },\n\n list: function(opt){\n if(!opt) opt={};\n return (opt.active) ? angular.copy(keymap) : angular.copy(defaultHotkeyMap);\n },\n\n keyAction: function(keySpec){\n return (keymap[keySpec]||[])[0];\n }\n\n };\n}]);\n\n// TODO refactor into a separate module\n\nmodule.factory('communityContentSvc', [\"$resource\", \"configService\", \"userService\", \"$rootScope\", function($resource, configService, userService, $rootScope) {\n var svc = {\n siteKey: configService.CommunitySiteKey,\n siteName: configService.CommunitySiteName,\n userIsAuthenticated: false,\n userId: null,\n userRatings: null,\n baseUrl: 'https://g9wczm9itj.execute-api.us-east-1.amazonaws.com/test',\n ratingsCache: {},\n };\n\n svc.api = {\n userRatings: $resource(svc.baseUrl + '/api/user/:id/ratings', {site_id: svc.siteKey}, {\n getList: {\n method: 'GET',\n isArray: true\n }\n }),\n rating: $resource(svc.baseUrl + '/api/work/:isbn/rating', {site_id: svc.siteKey}, {\n set: {\n method: 'POST',\n url: svc.baseUrl + '/api/work/:isbn/rating?user_id=:user_id&rating=:rating',\n },\n }),\n comments: $resource(svc.baseUrl + '/api/work/:isbn/comments', {site_id: svc.siteKey}, {\n getList: {\n method: 'GET',\n isArray: true\n },\n post: {\n method: 'POST'\n },\n }),\n \n };\n\n\n svc.getRating = function(isbn) {\n /*if (isbn in svc.ratingsCache) {\n return $q.when(svc.ratingsCache[isbn]);\n }*/\n\n return svc.api.rating.get({isbn: isbn}).$promise.then(function(rating) {\n svc.ratingsCache[isbn] = rating;\n return rating;\n });\n };\n\n svc.getUserRating = function(isbn) {\n return userService.whenGetUserId().then(function(userId) {\n if (userId <= 0) {\n return null;\n }\n else if (svc.userRatings) {\n return svc.userRatings[isbn];\n }\n else {\n return svc.api.userRatings.getList({id: userId}).$promise.then(function(ratings) {\n svc.userRatings = {};\n ratings.forEach(function(r) {\n svc.userRatings[r.work_id] = r.rating;\n });\n\n return svc.userRatings[isbn];\n }, function(err) {\n console.log(\"Caught error \" + err);\n console.dir(err);\n // TODO\n });\n }\n });\n };\n\n svc.setUserRating = function(isbn, rating) {\n return svc.api.rating.set({isbn: isbn, rating: rating, user_id: userService.id}, {}).$promise;\n };\n\n svc.getComments = function(isbn) {\n return svc.api.comments.getList({isbn: isbn}).$promise.then(function(comments) {\n return comments.sort(function(a,b) {\n if (a.created_time < b.created_time) return 1;\n else if (a.created_time > b.created_time) return -1;\n else return 0;\n });\n });\n };\n\n svc.addComment = function(isbn, comment) {\n return userService.whenAnyUserDetails().then(function(details) {\n console.dir(details);\n return svc.api.comments.post({isbn: isbn, user_id: userService.id}, {\n user_name: details.firstname + ' ' + details.surname,\n site_name: svc.siteName,\n text: comment\n }).$promise;\n });\n };\n \n $rootScope.$on('loggedin', function() {\n svc.getUserRating();\n });\n\n $rootScope.$on('loggedout', function() {\n svc.userRatings = null;\n svc.ratingsCache = {};\n });\n\n\n return svc;\n}]);\n\nmodule.factory('bvDownloadSvc', [ () => {\n let svc = {};\n\n svc.fetch = (conf) => {\n let data = conf.fetchData(),\n octetStreamMime = 'application/octet-stream',\n filename = conf.fileName,\n blob = new Blob([data], { type: octetStreamMime });\n\n if (navigator.msSaveBlob) {\n navigator.msSaveBlob(blob, filename);\n }\n else {\n let urlCreator = window.URL || window.webkitURL || window.mozURL || window.msURL;\n let url;\n if (urlCreator){\n let link = document.createElement('a');\n if ('download' in link) {\n url = urlCreator.createObjectURL(blob);\n let event = document.createEvent('MouseEvents');\n event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);\n link.setAttribute('href', url);\n link.setAttribute('download', filename);\n link.dispatchEvent(event);\n }\n else {\n url = urlCreator.createObjectURL(blob);\n window.location = url;\n }\n }\n else {\n console.log('Your browser does not support data download: ' + navigator.userAgent);\n }\n }\n };\n\n return svc;\n}]);\n\nmodule.factory('bvPrintSvc', [\"$http\", \"$rootScope\", \"$templateCache\", \"$compile\", \"$timeout\", function($http, $rootScope, $templateCache, $compile, $timeout) {\n var svc = {};\n\n svc.print = function(def) {\n // Currently only supports controller and templateUrl\n //\n var scope = $rootScope.$new(true);\n// $http.get(def.templateUrl, {cache: $templateCache}).then(function(resp) {\n $http.get(def.templateUrl, {cache: $templateCache}).then(function(resp) {\n def.controller(scope);\n\n var elem = $compile('

' + resp.data + '
')(scope);\n // Trigger a digest cycle\n $timeout(function() {\n var html = elem.html();\n var win = window.open('','_blank');\n win.document.write(html);\n win.document.close();\n win.setTimeout(function() {\n win.print();\n win.close();\n }, 500);\n });\n });\n };\n\n return svc;\n}]);\n\nmodule.factory('syncTemplateCache', function() {\n var svc = {\n cache: {}\n };\n\n svc.put = function(path, template) {\n if (path.substring(0,1) !== '/')\n path = '/' + path;\n path = path.replace(/^\\/app\\/partials/,'/app/static/partials');\n //console.log(\"Put template \" + path);\n svc.cache[path] = template;\n };\n\n svc.get = function(path) {\n if (path.substring(0,1) !== '/')\n path = '/' + path;\n //console.log(\"Get template \" + path);\n if (path in svc.cache)\n return svc.cache[path];\n else {\n console.log(\"Synchronous template \" + path + \" not found\");\n return '
synchronous template ' + path + ' not found
';\n }\n };\n\n return svc;\n});\n\n// For the bvAsyncOnready and bvAsyncOnreadyAwait directives\nmodule.factory('bvAsyncOnreadySvc', function() {\n var svc = {\n stat: {}\n };\n\n svc.register = function(name) {\n if (!svc.stat[name]) {\n svc.stat[name] = {child: {}};\n }\n svc.stat[name].templateReady = false;\n };\n\n svc.templateReady = function(name, onReady) {\n svc.stat[name].templateReady = true;\n svc.stat[name].onReady = onReady;\n svc.testIfReady(name);\n };\n\n\n svc.child = function(name, childName) {\n if (!svc.stat[name]) {\n svc.stat[name] = {child: {}};\n }\n\n if (!svc.stat[name].child[childName]) {\n svc.stat[name].child[childName] = 0;\n }\n svc.stat[name].child[childName]++;\n };\n\n svc.childReady = function(name, childName) {\n if (!svc.stat[name]) {\n svc.stat[name] = {child: {}};\n }\n\n if (!svc.stat[name].child[childName]) {\n svc.stat[name].child[childName] = 0;\n }\n svc.stat[name].child[childName]--;\n //console.log(\"Child \" + name + '.' + childName + \" = \" + svc.stat[name].child[childName]);\n\n svc.testIfReady(name);\n };\n\n svc.testIfReady = function(name) {\n var isReady = true;\n angular.forEach(svc.stat[name].child, function(val, key) {\n if (val > 0)\n isReady = false;\n });\n if (!isReady) return;\n if (!svc.stat[name].templateReady) return;\n //console.log(\"Firing \" + name + \" = \" + svc.stat[name].onReady);\n svc.stat[name].onReady();\n };\n\n return svc;\n});\n\n})();\n\n","/*\n * Generated by PEG.js 0.10.0.\n *\n * http://pegjs.org/\n */\n(function(root) {\n \"use strict\";\n\n function peg$subclass(child, parent) {\n function ctor() { this.constructor = child; }\n ctor.prototype = parent.prototype;\n child.prototype = new ctor();\n }\n\n function peg$SyntaxError(message, expected, found, location) {\n this.message = message;\n this.expected = expected;\n this.found = found;\n this.location = location;\n this.name = \"SyntaxError\";\n\n if (typeof Error.captureStackTrace === \"function\") {\n Error.captureStackTrace(this, peg$SyntaxError);\n }\n }\n\n peg$subclass(peg$SyntaxError, Error);\n\n peg$SyntaxError.buildMessage = function(expected, found) {\n var DESCRIBE_EXPECTATION_FNS = {\n literal: function(expectation) {\n return \"\\\"\" + literalEscape(expectation.text) + \"\\\"\";\n },\n\n \"class\": function(expectation) {\n var escapedParts = \"\",\n i;\n\n for (i = 0; i < expectation.parts.length; i++) {\n escapedParts += expectation.parts[i] instanceof Array\n ? classEscape(expectation.parts[i][0]) + \"-\" + classEscape(expectation.parts[i][1])\n : classEscape(expectation.parts[i]);\n }\n\n return \"[\" + (expectation.inverted ? \"^\" : \"\") + escapedParts + \"]\";\n },\n\n any: function(expectation) {\n return \"any character\";\n },\n\n end: function(expectation) {\n return \"end of input\";\n },\n\n other: function(expectation) {\n return expectation.description;\n }\n };\n\n function hex(ch) {\n return ch.charCodeAt(0).toString(16).toUpperCase();\n }\n\n function literalEscape(s) {\n return s\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\0/g, '\\\\0')\n .replace(/\\t/g, '\\\\t')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n .replace(/[\\x00-\\x0F]/g, function(ch) { return '\\\\x0' + hex(ch); })\n .replace(/[\\x10-\\x1F\\x7F-\\x9F]/g, function(ch) { return '\\\\x' + hex(ch); });\n }\n\n function classEscape(s) {\n return s\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\\]/g, '\\\\]')\n .replace(/\\^/g, '\\\\^')\n .replace(/-/g, '\\\\-')\n .replace(/\\0/g, '\\\\0')\n .replace(/\\t/g, '\\\\t')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n .replace(/[\\x00-\\x0F]/g, function(ch) { return '\\\\x0' + hex(ch); })\n .replace(/[\\x10-\\x1F\\x7F-\\x9F]/g, function(ch) { return '\\\\x' + hex(ch); });\n }\n\n function describeExpectation(expectation) {\n return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation);\n }\n\n function describeExpected(expected) {\n var descriptions = new Array(expected.length),\n i, j;\n\n for (i = 0; i < expected.length; i++) {\n descriptions[i] = describeExpectation(expected[i]);\n }\n\n descriptions.sort();\n\n if (descriptions.length > 0) {\n for (i = 1, j = 1; i < descriptions.length; i++) {\n if (descriptions[i - 1] !== descriptions[i]) {\n descriptions[j] = descriptions[i];\n j++;\n }\n }\n descriptions.length = j;\n }\n\n switch (descriptions.length) {\n case 1:\n return descriptions[0];\n\n case 2:\n return descriptions[0] + \" or \" + descriptions[1];\n\n default:\n return descriptions.slice(0, -1).join(\", \")\n + \", or \"\n + descriptions[descriptions.length - 1];\n }\n }\n\n function describeFound(found) {\n return found ? \"\\\"\" + literalEscape(found) + \"\\\"\" : \"end of input\";\n }\n\n return \"Expected \" + describeExpected(expected) + \" but \" + describeFound(found) + \" found.\";\n };\n\n function peg$parse(input, options) {\n options = options !== void 0 ? options : {};\n\n var peg$FAILED = {},\n\n peg$startRuleFunctions = { start: peg$parsestart },\n peg$startRuleFunction = peg$parsestart,\n\n peg$c0 = function(root) { return root; },\n peg$c1 = function(x) {\n return x;\n },\n peg$c2 = \"(\",\n peg$c3 = peg$literalExpectation(\"(\", false),\n peg$c4 = \")\",\n peg$c5 = peg$literalExpectation(\")\", false),\n peg$c6 = function(expr) {\n if(expr.length===1){\n expr[0].parens = true;\n return expr[0];\n } else {\n return { parens: true, terms: expr }\n }\n },\n peg$c7 = function(a, w) {\n var ors = a.map(function(x){return x[0];}).concat(w);\n return {\n op: 'OR',\n terms: ors\n }\n },\n peg$c8 = function(a, w) {\n var ands = a.map(function(x){return x[0];}).concat(w);\n return {\n op: 'AND',\n terms: a.map(function(x){return x[0];}).concat(w)\n }\n },\n peg$c9 = \"AND\",\n peg$c10 = peg$literalExpectation(\"AND\", false),\n peg$c11 = \"OR\",\n peg$c12 = peg$literalExpectation(\"OR\", false),\n peg$c13 = function(fieldname, range) {\n range.field = fieldname;\n return range;\n },\n peg$c14 = function(op, fieldname, term) {\n // term will be string or array.\n var fieldq = {\n field: fieldname,\n q: term\n };\n if(op) fieldq.prefix = op;\n return fieldq;\n },\n peg$c15 = function(spacedterms) {\n return spacedterms.map(function(t){ return t[1]; });\n },\n peg$c16 = /^[:]/,\n peg$c17 = peg$classExpectation([\":\"], false, false),\n peg$c18 = function(fieldname) {\n return fieldname;\n },\n peg$c19 = function(term) {\n return term.join('');\n },\n peg$c20 = \"\\\"\",\n peg$c21 = peg$literalExpectation(\"\\\"\", false),\n peg$c22 = /^[^\"]/,\n peg$c23 = peg$classExpectation([\"\\\"\"], true, false),\n peg$c24 = \".\",\n peg$c25 = peg$literalExpectation(\".\", false),\n peg$c26 = /^[^: \\t\\r\\n\\f{}()\"\\^~[\\]]/,\n peg$c27 = peg$classExpectation([\":\", \" \", \"\\t\", \"\\r\", \"\\n\", \"\\f\", \"{\", \"}\", \"(\", \")\", \"\\\"\", \"^\", \"~\", \"[\", \"]\"], true, false),\n peg$c28 = function(op, term, proximity, boost) {\n var result = {\n 'term': term,\n quotes: true\n };\n\n if(proximity) result['proximity'] = proximity;\n if(boost) result['boost'] = boost;\n if(op) result['prefix'] = op;\n return result;\n },\n peg$c29 = function(op, term, similarity, boost) {\n var result = { 'term': term };\n if(similarity) result['similarity'] = similarity;\n if(boost) result['boost'] = boost;\n if(op) result['prefix'] = op;\n return result;\n },\n peg$c30 = \"~\",\n peg$c31 = peg$literalExpectation(\"~\", false),\n peg$c32 = function(proximity) {\n return proximity;\n },\n peg$c33 = \"^\",\n peg$c34 = peg$literalExpectation(\"^\", false),\n peg$c35 = function(boost) {\n return boost;\n },\n peg$c36 = function(fuzziness) {\n return fuzziness == '' ? 0.5 : fuzziness;\n },\n peg$c37 = \"0.\",\n peg$c38 = peg$literalExpectation(\"0.\", false),\n peg$c39 = /^[0-9]/,\n peg$c40 = peg$classExpectation([[\"0\", \"9\"]], false, false),\n peg$c41 = function(val) {\n return parseFloat(\"0.\" + val.join(''));\n },\n peg$c42 = function(val) {\n return parseInt(val.join(''));\n },\n peg$c43 = \"+\",\n peg$c44 = peg$literalExpectation(\"+\", false),\n peg$c45 = \"-\",\n peg$c46 = peg$literalExpectation(\"-\", false),\n peg$c47 = function(p, spacer) {\n return p;\n },\n peg$c48 = /^[[{]/,\n peg$c49 = peg$classExpectation([\"[\", \"{\"], false, false),\n peg$c50 = \"TO\",\n peg$c51 = peg$literalExpectation(\"TO\", false),\n peg$c52 = /^[}\\]]/,\n peg$c53 = peg$classExpectation([\"}\", \"]\"], false, false),\n peg$c54 = function(left, term_min, term_max, right) {\n return {\n 'term_min': term_min,\n 'term_max': term_max,\n 'inclusive_l': left=='[',\n 'inclusive_r': right==']'\n };\n },\n peg$c55 = /^[ \\t\\r\\n\\f]/,\n peg$c56 = peg$classExpectation([\" \", \"\\t\", \"\\r\", \"\\n\", \"\\f\"], false, false),\n\n peg$currPos = 0,\n peg$savedPos = 0,\n peg$posDetailsCache = [{ line: 1, column: 1 }],\n peg$maxFailPos = 0,\n peg$maxFailExpected = [],\n peg$silentFails = 0,\n\n peg$result;\n\n if (\"startRule\" in options) {\n if (!(options.startRule in peg$startRuleFunctions)) {\n throw new Error(\"Can't start parsing from rule \\\"\" + options.startRule + \"\\\".\");\n }\n\n peg$startRuleFunction = peg$startRuleFunctions[options.startRule];\n }\n\n function text() {\n return input.substring(peg$savedPos, peg$currPos);\n }\n\n function location() {\n return peg$computeLocation(peg$savedPos, peg$currPos);\n }\n\n function expected(description, location) {\n location = location !== void 0 ? location : peg$computeLocation(peg$savedPos, peg$currPos)\n\n throw peg$buildStructuredError(\n [peg$otherExpectation(description)],\n input.substring(peg$savedPos, peg$currPos),\n location\n );\n }\n\n function error(message, location) {\n location = location !== void 0 ? location : peg$computeLocation(peg$savedPos, peg$currPos)\n\n throw peg$buildSimpleError(message, location);\n }\n\n function peg$literalExpectation(text, ignoreCase) {\n return { type: \"literal\", text: text, ignoreCase: ignoreCase };\n }\n\n function peg$classExpectation(parts, inverted, ignoreCase) {\n return { type: \"class\", parts: parts, inverted: inverted, ignoreCase: ignoreCase };\n }\n\n function peg$anyExpectation() {\n return { type: \"any\" };\n }\n\n function peg$endExpectation() {\n return { type: \"end\" };\n }\n\n function peg$otherExpectation(description) {\n return { type: \"other\", description: description };\n }\n\n function peg$computePosDetails(pos) {\n var details = peg$posDetailsCache[pos], p;\n\n if (details) {\n return details;\n } else {\n p = pos - 1;\n while (!peg$posDetailsCache[p]) {\n p--;\n }\n\n details = peg$posDetailsCache[p];\n details = {\n line: details.line,\n column: details.column\n };\n\n while (p < pos) {\n if (input.charCodeAt(p) === 10) {\n details.line++;\n details.column = 1;\n } else {\n details.column++;\n }\n\n p++;\n }\n\n peg$posDetailsCache[pos] = details;\n return details;\n }\n }\n\n function peg$computeLocation(startPos, endPos) {\n var startPosDetails = peg$computePosDetails(startPos),\n endPosDetails = peg$computePosDetails(endPos);\n\n return {\n start: {\n offset: startPos,\n line: startPosDetails.line,\n column: startPosDetails.column\n },\n end: {\n offset: endPos,\n line: endPosDetails.line,\n column: endPosDetails.column\n }\n };\n }\n\n function peg$fail(expected) {\n if (peg$currPos < peg$maxFailPos) { return; }\n\n if (peg$currPos > peg$maxFailPos) {\n peg$maxFailPos = peg$currPos;\n peg$maxFailExpected = [];\n }\n\n peg$maxFailExpected.push(expected);\n }\n\n function peg$buildSimpleError(message, location) {\n return new peg$SyntaxError(message, null, null, location);\n }\n\n function peg$buildStructuredError(expected, found, location) {\n return new peg$SyntaxError(\n peg$SyntaxError.buildMessage(expected, found),\n expected,\n found,\n location\n );\n }\n\n function peg$parsestart() {\n var s0, s1, s2;\n\n s0 = peg$currPos;\n s1 = [];\n s2 = peg$parseexpr();\n if (s2 !== peg$FAILED) {\n while (s2 !== peg$FAILED) {\n s1.push(s2);\n s2 = peg$parseexpr();\n }\n } else {\n s1 = peg$FAILED;\n }\n if (s1 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c0(s1);\n }\n s0 = s1;\n\n return s0;\n }\n\n function peg$parseexpr() {\n var s0, s1, s2, s3, s4;\n\n s0 = peg$currPos;\n s1 = [];\n s2 = peg$parse_();\n while (s2 !== peg$FAILED) {\n s1.push(s2);\n s2 = peg$parse_();\n }\n if (s1 !== peg$FAILED) {\n s2 = peg$parseorExpr();\n if (s2 === peg$FAILED) {\n s2 = peg$parseandExpr();\n if (s2 === peg$FAILED) {\n s2 = peg$parsegrouped();\n if (s2 === peg$FAILED) {\n s2 = peg$parsefield_query();\n }\n }\n }\n if (s2 !== peg$FAILED) {\n s3 = [];\n s4 = peg$parse_();\n while (s4 !== peg$FAILED) {\n s3.push(s4);\n s4 = peg$parse_();\n }\n if (s3 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c1(s2);\n s0 = s1;\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n\n return s0;\n }\n\n function peg$parsegrouped() {\n var s0, s1, s2, s3;\n\n s0 = peg$currPos;\n if (input.charCodeAt(peg$currPos) === 40) {\n s1 = peg$c2;\n peg$currPos++;\n } else {\n s1 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c3); }\n }\n if (s1 !== peg$FAILED) {\n s2 = [];\n s3 = peg$parseexpr();\n if (s3 !== peg$FAILED) {\n while (s3 !== peg$FAILED) {\n s2.push(s3);\n s3 = peg$parseexpr();\n }\n } else {\n s2 = peg$FAILED;\n }\n if (s2 !== peg$FAILED) {\n if (input.charCodeAt(peg$currPos) === 41) {\n s3 = peg$c4;\n peg$currPos++;\n } else {\n s3 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c5); }\n }\n if (s3 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c6(s2);\n s0 = s1;\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n\n return s0;\n }\n\n function peg$parseorExpr() {\n var s0, s1, s2, s3, s4;\n\n s0 = peg$currPos;\n s1 = [];\n s2 = peg$currPos;\n s3 = peg$parsegrouped();\n if (s3 === peg$FAILED) {\n s3 = peg$parseandExpr();\n if (s3 === peg$FAILED) {\n s3 = peg$parsefield_query();\n }\n }\n if (s3 !== peg$FAILED) {\n s4 = peg$parseor();\n if (s4 !== peg$FAILED) {\n s3 = [s3, s4];\n s2 = s3;\n } else {\n peg$currPos = s2;\n s2 = peg$FAILED;\n }\n } else {\n peg$currPos = s2;\n s2 = peg$FAILED;\n }\n if (s2 !== peg$FAILED) {\n while (s2 !== peg$FAILED) {\n s1.push(s2);\n s2 = peg$currPos;\n s3 = peg$parsegrouped();\n if (s3 === peg$FAILED) {\n s3 = peg$parseandExpr();\n if (s3 === peg$FAILED) {\n s3 = peg$parsefield_query();\n }\n }\n if (s3 !== peg$FAILED) {\n s4 = peg$parseor();\n if (s4 !== peg$FAILED) {\n s3 = [s3, s4];\n s2 = s3;\n } else {\n peg$currPos = s2;\n s2 = peg$FAILED;\n }\n } else {\n peg$currPos = s2;\n s2 = peg$FAILED;\n }\n }\n } else {\n s1 = peg$FAILED;\n }\n if (s1 !== peg$FAILED) {\n s2 = peg$parsegrouped();\n if (s2 === peg$FAILED) {\n s2 = peg$parseandExpr();\n if (s2 === peg$FAILED) {\n s2 = peg$parsefield_query();\n }\n }\n if (s2 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c7(s1, s2);\n s0 = s1;\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n\n return s0;\n }\n\n function peg$parseandExpr() {\n var s0, s1, s2, s3, s4;\n\n s0 = peg$currPos;\n s1 = [];\n s2 = peg$currPos;\n s3 = peg$parsegrouped();\n if (s3 === peg$FAILED) {\n s3 = peg$parsefield_query();\n }\n if (s3 !== peg$FAILED) {\n s4 = peg$parseand();\n if (s4 !== peg$FAILED) {\n s3 = [s3, s4];\n s2 = s3;\n } else {\n peg$currPos = s2;\n s2 = peg$FAILED;\n }\n } else {\n peg$currPos = s2;\n s2 = peg$FAILED;\n }\n if (s2 !== peg$FAILED) {\n while (s2 !== peg$FAILED) {\n s1.push(s2);\n s2 = peg$currPos;\n s3 = peg$parsegrouped();\n if (s3 === peg$FAILED) {\n s3 = peg$parsefield_query();\n }\n if (s3 !== peg$FAILED) {\n s4 = peg$parseand();\n if (s4 !== peg$FAILED) {\n s3 = [s3, s4];\n s2 = s3;\n } else {\n peg$currPos = s2;\n s2 = peg$FAILED;\n }\n } else {\n peg$currPos = s2;\n s2 = peg$FAILED;\n }\n }\n } else {\n s1 = peg$FAILED;\n }\n if (s1 !== peg$FAILED) {\n s2 = peg$parsegrouped();\n if (s2 === peg$FAILED) {\n s2 = peg$parsefield_query();\n }\n if (s2 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c8(s1, s2);\n s0 = s1;\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n\n return s0;\n }\n\n function peg$parseand() {\n var s0, s1, s2, s3, s4;\n\n s0 = peg$currPos;\n s1 = [];\n s2 = peg$parse_();\n if (s2 !== peg$FAILED) {\n while (s2 !== peg$FAILED) {\n s1.push(s2);\n s2 = peg$parse_();\n }\n } else {\n s1 = peg$FAILED;\n }\n if (s1 !== peg$FAILED) {\n if (input.substr(peg$currPos, 3) === peg$c9) {\n s2 = peg$c9;\n peg$currPos += 3;\n } else {\n s2 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c10); }\n }\n if (s2 !== peg$FAILED) {\n s3 = [];\n s4 = peg$parse_();\n if (s4 !== peg$FAILED) {\n while (s4 !== peg$FAILED) {\n s3.push(s4);\n s4 = peg$parse_();\n }\n } else {\n s3 = peg$FAILED;\n }\n if (s3 !== peg$FAILED) {\n s1 = [s1, s2, s3];\n s0 = s1;\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n\n return s0;\n }\n\n function peg$parseor() {\n var s0, s1, s2, s3, s4;\n\n s0 = peg$currPos;\n s1 = [];\n s2 = peg$parse_();\n if (s2 !== peg$FAILED) {\n while (s2 !== peg$FAILED) {\n s1.push(s2);\n s2 = peg$parse_();\n }\n } else {\n s1 = peg$FAILED;\n }\n if (s1 !== peg$FAILED) {\n if (input.substr(peg$currPos, 2) === peg$c11) {\n s2 = peg$c11;\n peg$currPos += 2;\n } else {\n s2 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c12); }\n }\n if (s2 !== peg$FAILED) {\n s3 = [];\n s4 = peg$parse_();\n if (s4 !== peg$FAILED) {\n while (s4 !== peg$FAILED) {\n s3.push(s4);\n s4 = peg$parse_();\n }\n } else {\n s3 = peg$FAILED;\n }\n if (s3 !== peg$FAILED) {\n s1 = [s1, s2, s3];\n s0 = s1;\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n\n return s0;\n }\n\n function peg$parsefield_query() {\n var s0, s1, s2, s3;\n\n s0 = peg$currPos;\n s1 = peg$parsefieldname();\n if (s1 === peg$FAILED) {\n s1 = null;\n }\n if (s1 !== peg$FAILED) {\n s2 = peg$parserange_operator_exp();\n if (s2 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c13(s1, s2);\n s0 = s1;\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n if (s0 === peg$FAILED) {\n s0 = peg$currPos;\n s1 = peg$parseprefix_operator();\n if (s1 === peg$FAILED) {\n s1 = null;\n }\n if (s1 !== peg$FAILED) {\n s2 = peg$parsefieldname();\n if (s2 === peg$FAILED) {\n s2 = null;\n }\n if (s2 !== peg$FAILED) {\n s3 = peg$parseparen_terms();\n if (s3 === peg$FAILED) {\n s3 = peg$parseterm();\n }\n if (s3 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c14(s1, s2, s3);\n s0 = s1;\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n }\n\n return s0;\n }\n\n function peg$parseparen_terms() {\n var s0, s1, s2, s3, s4, s5;\n\n s0 = peg$currPos;\n if (input.charCodeAt(peg$currPos) === 40) {\n s1 = peg$c2;\n peg$currPos++;\n } else {\n s1 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c3); }\n }\n if (s1 !== peg$FAILED) {\n s2 = [];\n s3 = peg$currPos;\n s4 = [];\n s5 = peg$parse_();\n while (s5 !== peg$FAILED) {\n s4.push(s5);\n s5 = peg$parse_();\n }\n if (s4 !== peg$FAILED) {\n s5 = peg$parseterm();\n if (s5 !== peg$FAILED) {\n s4 = [s4, s5];\n s3 = s4;\n } else {\n peg$currPos = s3;\n s3 = peg$FAILED;\n }\n } else {\n peg$currPos = s3;\n s3 = peg$FAILED;\n }\n if (s3 !== peg$FAILED) {\n while (s3 !== peg$FAILED) {\n s2.push(s3);\n s3 = peg$currPos;\n s4 = [];\n s5 = peg$parse_();\n while (s5 !== peg$FAILED) {\n s4.push(s5);\n s5 = peg$parse_();\n }\n if (s4 !== peg$FAILED) {\n s5 = peg$parseterm();\n if (s5 !== peg$FAILED) {\n s4 = [s4, s5];\n s3 = s4;\n } else {\n peg$currPos = s3;\n s3 = peg$FAILED;\n }\n } else {\n peg$currPos = s3;\n s3 = peg$FAILED;\n }\n }\n } else {\n s2 = peg$FAILED;\n }\n if (s2 !== peg$FAILED) {\n s3 = [];\n s4 = peg$parse_();\n while (s4 !== peg$FAILED) {\n s3.push(s4);\n s4 = peg$parse_();\n }\n if (s3 !== peg$FAILED) {\n if (input.charCodeAt(peg$currPos) === 41) {\n s4 = peg$c4;\n peg$currPos++;\n } else {\n s4 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c5); }\n }\n if (s4 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c15(s2);\n s0 = s1;\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n\n return s0;\n }\n\n function peg$parsefieldname() {\n var s0, s1, s2;\n\n s0 = peg$currPos;\n s1 = peg$parseunquoted_term();\n if (s1 !== peg$FAILED) {\n if (peg$c16.test(input.charAt(peg$currPos))) {\n s2 = input.charAt(peg$currPos);\n peg$currPos++;\n } else {\n s2 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c17); }\n }\n if (s2 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c18(s1);\n s0 = s1;\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n\n return s0;\n }\n\n function peg$parseunquoted_term() {\n var s0, s1, s2;\n\n s0 = peg$currPos;\n s1 = [];\n s2 = peg$parseterm_char();\n if (s2 !== peg$FAILED) {\n while (s2 !== peg$FAILED) {\n s1.push(s2);\n s2 = peg$parseterm_char();\n }\n } else {\n s1 = peg$FAILED;\n }\n if (s1 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c19(s1);\n }\n s0 = s1;\n\n return s0;\n }\n\n function peg$parsequoted_term() {\n var s0, s1, s2, s3;\n\n s0 = peg$currPos;\n if (input.charCodeAt(peg$currPos) === 34) {\n s1 = peg$c20;\n peg$currPos++;\n } else {\n s1 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c21); }\n }\n if (s1 !== peg$FAILED) {\n s2 = [];\n if (peg$c22.test(input.charAt(peg$currPos))) {\n s3 = input.charAt(peg$currPos);\n peg$currPos++;\n } else {\n s3 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c23); }\n }\n if (s3 !== peg$FAILED) {\n while (s3 !== peg$FAILED) {\n s2.push(s3);\n if (peg$c22.test(input.charAt(peg$currPos))) {\n s3 = input.charAt(peg$currPos);\n peg$currPos++;\n } else {\n s3 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c23); }\n }\n }\n } else {\n s2 = peg$FAILED;\n }\n if (s2 !== peg$FAILED) {\n if (input.charCodeAt(peg$currPos) === 34) {\n s3 = peg$c20;\n peg$currPos++;\n } else {\n s3 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c21); }\n }\n if (s3 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c19(s2);\n s0 = s1;\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n\n return s0;\n }\n\n function peg$parseterm_char() {\n var s0;\n\n if (input.charCodeAt(peg$currPos) === 46) {\n s0 = peg$c24;\n peg$currPos++;\n } else {\n s0 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c25); }\n }\n if (s0 === peg$FAILED) {\n if (peg$c26.test(input.charAt(peg$currPos))) {\n s0 = input.charAt(peg$currPos);\n peg$currPos++;\n } else {\n s0 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c27); }\n }\n }\n\n return s0;\n }\n\n function peg$parseterm() {\n var s0, s1, s2, s3, s4;\n\n s0 = peg$currPos;\n s1 = peg$parseprefix_operator();\n if (s1 === peg$FAILED) {\n s1 = null;\n }\n if (s1 !== peg$FAILED) {\n s2 = peg$parsequoted_term();\n if (s2 !== peg$FAILED) {\n s3 = peg$parseproximity_modifier();\n if (s3 === peg$FAILED) {\n s3 = null;\n }\n if (s3 !== peg$FAILED) {\n s4 = peg$parseboost_modifier();\n if (s4 === peg$FAILED) {\n s4 = null;\n }\n if (s4 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c28(s1, s2, s3, s4);\n s0 = s1;\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n if (s0 === peg$FAILED) {\n s0 = peg$currPos;\n s1 = peg$parseprefix_operator();\n if (s1 === peg$FAILED) {\n s1 = null;\n }\n if (s1 !== peg$FAILED) {\n s2 = peg$parseunquoted_term();\n if (s2 !== peg$FAILED) {\n s3 = peg$parsefuzzy_modifier();\n if (s3 === peg$FAILED) {\n s3 = null;\n }\n if (s3 !== peg$FAILED) {\n s4 = peg$parseboost_modifier();\n if (s4 === peg$FAILED) {\n s4 = null;\n }\n if (s4 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c29(s1, s2, s3, s4);\n s0 = s1;\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n }\n\n return s0;\n }\n\n function peg$parseproximity_modifier() {\n var s0, s1, s2;\n\n s0 = peg$currPos;\n if (input.charCodeAt(peg$currPos) === 126) {\n s1 = peg$c30;\n peg$currPos++;\n } else {\n s1 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c31); }\n }\n if (s1 !== peg$FAILED) {\n s2 = peg$parseint_exp();\n if (s2 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c32(s2);\n s0 = s1;\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n\n return s0;\n }\n\n function peg$parseboost_modifier() {\n var s0, s1, s2;\n\n s0 = peg$currPos;\n if (input.charCodeAt(peg$currPos) === 94) {\n s1 = peg$c33;\n peg$currPos++;\n } else {\n s1 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c34); }\n }\n if (s1 !== peg$FAILED) {\n s2 = peg$parsedecimal_or_int_exp();\n if (s2 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c35(s2);\n s0 = s1;\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n\n return s0;\n }\n\n function peg$parsefuzzy_modifier() {\n var s0, s1, s2;\n\n s0 = peg$currPos;\n if (input.charCodeAt(peg$currPos) === 126) {\n s1 = peg$c30;\n peg$currPos++;\n } else {\n s1 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c31); }\n }\n if (s1 !== peg$FAILED) {\n s2 = peg$parsedecimal_exp();\n if (s2 === peg$FAILED) {\n s2 = null;\n }\n if (s2 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c36(s2);\n s0 = s1;\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n\n return s0;\n }\n\n function peg$parsedecimal_or_int_exp() {\n var s0;\n\n s0 = peg$parsedecimal_exp();\n if (s0 === peg$FAILED) {\n s0 = peg$parseint_exp();\n }\n\n return s0;\n }\n\n function peg$parsedecimal_exp() {\n var s0, s1, s2, s3;\n\n s0 = peg$currPos;\n if (input.substr(peg$currPos, 2) === peg$c37) {\n s1 = peg$c37;\n peg$currPos += 2;\n } else {\n s1 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c38); }\n }\n if (s1 !== peg$FAILED) {\n s2 = [];\n if (peg$c39.test(input.charAt(peg$currPos))) {\n s3 = input.charAt(peg$currPos);\n peg$currPos++;\n } else {\n s3 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c40); }\n }\n if (s3 !== peg$FAILED) {\n while (s3 !== peg$FAILED) {\n s2.push(s3);\n if (peg$c39.test(input.charAt(peg$currPos))) {\n s3 = input.charAt(peg$currPos);\n peg$currPos++;\n } else {\n s3 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c40); }\n }\n }\n } else {\n s2 = peg$FAILED;\n }\n if (s2 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c41(s2);\n s0 = s1;\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n\n return s0;\n }\n\n function peg$parseint_exp() {\n var s0, s1, s2;\n\n s0 = peg$currPos;\n s1 = [];\n if (peg$c39.test(input.charAt(peg$currPos))) {\n s2 = input.charAt(peg$currPos);\n peg$currPos++;\n } else {\n s2 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c40); }\n }\n if (s2 !== peg$FAILED) {\n while (s2 !== peg$FAILED) {\n s1.push(s2);\n if (peg$c39.test(input.charAt(peg$currPos))) {\n s2 = input.charAt(peg$currPos);\n peg$currPos++;\n } else {\n s2 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c40); }\n }\n }\n } else {\n s1 = peg$FAILED;\n }\n if (s1 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c42(s1);\n }\n s0 = s1;\n\n return s0;\n }\n\n function peg$parseprefix_operator() {\n var s0, s1, s2, s3;\n\n s0 = peg$currPos;\n if (input.charCodeAt(peg$currPos) === 43) {\n s1 = peg$c43;\n peg$currPos++;\n } else {\n s1 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c44); }\n }\n if (s1 === peg$FAILED) {\n if (input.charCodeAt(peg$currPos) === 45) {\n s1 = peg$c45;\n peg$currPos++;\n } else {\n s1 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c46); }\n }\n }\n if (s1 !== peg$FAILED) {\n s2 = peg$currPos;\n peg$silentFails++;\n s3 = peg$parse_();\n peg$silentFails--;\n if (s3 === peg$FAILED) {\n s2 = void 0;\n } else {\n peg$currPos = s2;\n s2 = peg$FAILED;\n }\n if (s2 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c47(s1, s2);\n s0 = s1;\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n\n return s0;\n }\n\n function peg$parserange_operator_exp() {\n var s0, s1, s2, s3, s4, s5, s6, s7;\n\n s0 = peg$currPos;\n if (peg$c48.test(input.charAt(peg$currPos))) {\n s1 = input.charAt(peg$currPos);\n peg$currPos++;\n } else {\n s1 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c49); }\n }\n if (s1 !== peg$FAILED) {\n s2 = peg$parseunquoted_term();\n if (s2 !== peg$FAILED) {\n s3 = [];\n s4 = peg$parse_();\n while (s4 !== peg$FAILED) {\n s3.push(s4);\n s4 = peg$parse_();\n }\n if (s3 !== peg$FAILED) {\n if (input.substr(peg$currPos, 2) === peg$c50) {\n s4 = peg$c50;\n peg$currPos += 2;\n } else {\n s4 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c51); }\n }\n if (s4 !== peg$FAILED) {\n s5 = [];\n s6 = peg$parse_();\n if (s6 !== peg$FAILED) {\n while (s6 !== peg$FAILED) {\n s5.push(s6);\n s6 = peg$parse_();\n }\n } else {\n s5 = peg$FAILED;\n }\n if (s5 !== peg$FAILED) {\n s6 = peg$parseunquoted_term();\n if (s6 !== peg$FAILED) {\n if (peg$c52.test(input.charAt(peg$currPos))) {\n s7 = input.charAt(peg$currPos);\n peg$currPos++;\n } else {\n s7 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c53); }\n }\n if (s7 !== peg$FAILED) {\n peg$savedPos = s0;\n s1 = peg$c54(s1, s2, s6, s7);\n s0 = s1;\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n } else {\n peg$currPos = s0;\n s0 = peg$FAILED;\n }\n\n return s0;\n }\n\n function peg$parse_() {\n var s0, s1;\n\n s0 = [];\n if (peg$c55.test(input.charAt(peg$currPos))) {\n s1 = input.charAt(peg$currPos);\n peg$currPos++;\n } else {\n s1 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c56); }\n }\n if (s1 !== peg$FAILED) {\n while (s1 !== peg$FAILED) {\n s0.push(s1);\n if (peg$c55.test(input.charAt(peg$currPos))) {\n s1 = input.charAt(peg$currPos);\n peg$currPos++;\n } else {\n s1 = peg$FAILED;\n if (peg$silentFails === 0) { peg$fail(peg$c56); }\n }\n }\n } else {\n s0 = peg$FAILED;\n }\n\n return s0;\n }\n\n peg$result = peg$startRuleFunction();\n\n if (peg$result !== peg$FAILED && peg$currPos === input.length) {\n return peg$result;\n } else {\n if (peg$result !== peg$FAILED && peg$currPos < input.length) {\n peg$fail(peg$endExpectation());\n }\n\n throw peg$buildStructuredError(\n peg$maxFailExpected,\n peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null,\n peg$maxFailPos < input.length\n ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1)\n : peg$computeLocation(peg$maxFailPos, peg$maxFailPos)\n );\n }\n }\n\n root.kwLuceneParser = {\n SyntaxError: peg$SyntaxError,\n parse: peg$parse\n };\n})(this);\n"," \n\"use strict\";\n\nangular.module('kohapac.kwLuceneParser', [])\n\n.service('kwLuceneParser', function(){\n\n if(!window.kwLuceneParser)\n throw( \"No query parser found.\");\n\n var parser = window.kwLuceneParser;\n // kwLuceneParser is generated from lucene-query-parser.pegjs grammar def.\n // use scripts/make-lucene-parser.sh\n\n this.parse = function(q){\n try {\n return parser.parse(q);\n } catch(e) {\n console.warn(e);\n return [{\n field: null,\n term: q\n }]\n }\n }\n this.SyntaxError = parser.SyntaxError;\n\n var self = this;\n\n this.stringify = function(parsed_q){\n if(!Array.isArray(parsed_q)) throw \"Invalid parsed query object\";\n return parsed_q.map(function(q){ return stringifyNode(q); }).join(' ');\n };\n\n this.extractSubqueries = function(q_str){\n // simplified subquery parsing. returns a non-nested array of field queries with optional operator.\n var parsed;\n try{\n parsed = this.parse(q_str);\n } catch (e){\n console.warn(e);\n return {\n op: 'OR',\n subqueries: [ { field: null, q: q_str }]\n };\n }\n var op;\n var qterms;\n if(parsed.length==1 && parsed[0].op){\n op = parsed[0].op;\n qterms = parsed[0].terms;\n } else {\n qterms = parsed;\n }\n var subqueries = [];\n\n return {\n op: op,\n subqueries: qterms.map(function(sq){\n var as_str = stringifyNode(sq);\n as_str = as_str.replace(sq.field + ':','');\n return { field: sq.field, q: as_str };\n })\n };\n\n };\n this.composeSubqueries = function(subqueries){\n // we ignore the op, assuming we pass it as a query param for now.\n var query_array = [];\n\n subqueries.forEach(function(sq){\n if(sq.q){\n try{\n query_array.push( { field: sq.field, q: self.parse(sq.q) } );\n } catch(e) {\n console.warn(e);\n }\n }\n });\n return self.stringify(query_array);\n };\n\n this.extractTerms = function(parsed_q){\n if(!Array.isArray(parsed_q)) throw \"Invalid parsed query object\";\n var terms = [];\n parsed_q.forEach(function(q){ getTerms(q, terms); });\n return terms;\n };\n\n function stringifyNode(node){\n // a node is either a field expr (or set of them)\n // or a term expr.\n var n = angular.copy(node);\n\n var str = '';\n if('q' in n){\n\n // field expression.\n if(n.field){\n str = (n.prefix||'') + n.field + ':';\n }\n\n var parens = true;\n if( ! Array.isArray(n.q)){\n parens = false;\n n.q = [ n.q ];\n }\n\n var terms = n.q.map(function(q){ return stringifyNode(q); }).join(' ');\n if(n.q.length > 1) terms = \"(\" + terms + \")\";\n str += terms;\n } else if('terms' in n){\n // term group expr\n str += n.terms.map( function(t){ return stringifyNode(t); }).\n join( (n.op) ? (' '+n.op+' ') : ' ');\n if(n.parens) str = \"(\"+str+\")\";\n } else {\n // term expr\n\n str = n.term;\n if(n.quotes) str = '\"'+str+'\"';\n if(n.proximity||n.similarity) str += (\"~\"+n.proximity||n.similarity);\n if(n.boost) str += '^'+n.boost;\n if(n.prefix) str = n.prefix + str;\n }\n return str;\n }\n\n // get all query terms from parsed query, without fields (for term highlighting)\n function getTerms ( node, termlist){\n if(!termlist) termlist = [];\n var n = angular.copy(node);\n if('q' in n){ // field expression.\n var nq = Array.isArray(n.q) ? n.q : [ n.q ];\n nq.forEach( function(q){ getTerms(q, termlist); } );\n } else if('terms' in n){ // term group expr\n n.terms.forEach( function(t){ getTerms(t, termlist); });\n } else { // term expr\n termlist.push( n.term );\n // may need to get rid of operators ??\n }\n return termlist;\n }\n\n});\n","(function () {\n\n 'use strict';\n\n /* Controllers */\n\n angular.module('kohapac.controllers', [])\n\n .controller('LandingPageCtrl', [\"$scope\", \"$sce\", \"$http\", function ($scope, $sce, $http) {\n $scope.opacnews = [];\n $http.get('/api/opac-news', {\n cache: true\n }).then(function (rsp) {\n var news = [];\n for (var id in rsp.data) {\n news.push({\n title: rsp.data[id].title,\n entry: $sce.trustAsHtml(rsp.data[id]['new']),\n date: rsp.data[id].timestamp,\n order: Number(rsp.data[id].number)\n });\n }\n $scope.opacnews = news.sort(function (a, b) {\n return a.order - b.order;\n });\n });\n\n }])\n .controller('AppErrorCtrl', [\"$scope\", \"$stateParams\", function($scope, $stateParams ){\n\n console.warn($stateParams);\n $scope.errorcode = $stateParams.code;\n\n }])\n\n .controller('SearchCtrl', [\"$scope\", \"$state\", \"$timeout\", \"SearchQuery\", \"userService\", \"kohaSearchSvc\", \"kwLuceneParser\", \"configService\", function ($scope, $state, $timeout, SearchQuery, userService, kohaSearchSvc, kwLuceneParser, configService) {\n // advanced search page.\n\n $scope.sortOptions = (configService.opacConfig.mastheadSearchConfig.sortOptions)\n ? configService.opacConfig.mastheadSearchConfig.sortOptions : configService.sortoptions;\n $scope.sortOptions = $scope.sortOptions.map( function(opt){\n if (!opt.group) delete opt.group;\n return opt;\n });\n\n var lastSearch = kohaSearchSvc.currentSearch();\n\n $scope.op = '';\n\n $scope.searchFields = $scope.config.advancedSearch.queryFields.basic;\n\n $scope.subqueries = [ ];\n\n var oldlimits = {};\n// extract search from last query.\n// note this misses a few things.\n // TODO: Now that we have a query parser, we should build out a better adv search interface.\n\n if(lastSearch){\n var parsed_subqueries = kwLuceneParser.extractSubqueries(lastSearch.q);\n if(parsed_subqueries){\n if(parsed_subqueries.op=='OR') $scope.op = 'OR';\n parsed_subqueries.subqueries.forEach(function(sq){\n $scope.subqueries.push( sq );\n });\n }\n oldlimits = angular.copy(lastSearch.limitfields);\n }\n\n $scope.rmQuery = function (i) {\n $scope.subqueries.splice(i, 1);\n };\n $scope.addQuery = function () {\n $scope.subqueries.push({\n q: '',\n field: $scope.searchFields[0].field\n });\n };\n if(!$scope.subqueries.length) $scope.addQuery();\n\n $scope.options = {};\n angular.forEach({\n language: 'bv-language',\n branch: 'bv-branch',\n itemtype: 'bv-itemtype',\n eformat: 'eformat',\n ccode: 'ccode',\n loc: 'loc'\n }, function (interp, limit) {\n $scope.options[limit] = $scope.config.listCodes(interp).filter(function (code) {\n if (!$scope.config.advancedSearch[limit].exclude) return true;\n return ($scope.config.advancedSearch[limit].exclude.indexOf(code) == -1) ? true : false;\n });\n });\n var defaultSort = ($scope.config.OPACdefaultSortField || 'score') + ' ' + ($scope.config.OPACdefaultSortOrder || 'desc');\n\n $scope.clearLimits = function() {\n $scope.limits = {\n itemtype: [],\n collection: [],\n ln: [],\n branch: [],\n 'location': [],\n eformat: [],\n format: null,\n content: '',\n audience: '',\n pubyear: {\n from: '',\n to: ''\n },\n lexile_i: {\n from: '',\n to: ''\n },\n geoscale: {\n from: '',\n to: ''\n },\n rangeSearch : {},\n available: false,\n sort: ($scope.config.advancedSearch.sort.clearToDefault ? defaultSort : ((lastSearch||{}).sort || defaultSort)),\n suppress: ''\n };\n };\n $scope.clearLimits();\n\n angular.forEach(['itemtype', 'collection', 'ln', 'location', 'eformat'], function (limit) {\n if (oldlimits[limit] && oldlimits[limit].length == 1) {\n $scope.limits[limit] = oldlimits[limit][0].replace(/\"/g, '').split(' OR ');\n }\n });\n if (oldlimits['on-shelf-at'] && oldlimits['on-shelf-at'].length == 1) {\n $scope.limits.available = true;\n $scope.limits.branch = oldlimits['on-shelf-at'][0].replace(/\"/g, '').split(' OR ');\n } else if (oldlimits['owned-by'] && oldlimits['owned-by'].length == 1) {\n $scope.limits.branch = oldlimits['owned-by'][0].replace(/\"/g, '').split(' OR ');\n }\n if (oldlimits.pubyear && oldlimits.pubyear.length == 1) {\n var drange = oldlimits.pubyear[0].replace(/[\\[\\]]/g, '').split(' TO ');\n $scope.limits.pubyear.from = (drange[0] == '*') ? '' : drange[0];\n $scope.limits.pubyear.to = (drange[1] == '*') ? '' : drange[1];\n }\n\n $scope.clearMusicSearch = function() {\n $scope.musicSearch = {\n queries: [{\n instr: null,\n num: null\n }],\n op: 'AND',\n add: function () {\n this.queries.push({\n instr: null,\n num: null\n });\n },\n rm: function (i) {\n this.queries.splice(i, 1);\n },\n ensemble: null,\n toString: function () {\n //var str = '';\n return this.ensemble || this.queries.filter(function (q) {\n return q.instr && q.num;\n }).\n map(function (q) {\n return q.instr + ((q.num < 10) ? '0' + q.num : q.num);\n }).\n join(' ' + this.op + ' ');\n }\n };\n };\n $scope.clearMusicSearch();\n\n $scope.calendar = {\n opened: { 'create1': false,\n 'create2': false,\n 'load1': false,\n 'load2': false,\n },\n dateFormat: 'MM/dd/yyyy',\n open: function(which) {\n $timeout(function(){\n $scope.calendar.opened[which] = true;\n });\n }\n };\n\n $scope.clearAdvancedSearch = function() {\n $scope.subqueries = [{\n q: '',\n field: null\n }];\n $scope.op = '';\n $scope.clearLimits();\n $scope.clearMusicSearch();\n kohaSearchSvc.clearCurrentSearch();\n };\n if (!$scope.config.advancedSearch.retainOnEntry) $scope.clearAdvancedSearch();\n\n var getRange = function(from, to, opt){\n if(!opt) opt = {};\n if(!opt.format) opt.format = 'number';\n var formatter = {\n 'number': function(n){ return n || '*'; },\n 'datetime': function(d, from){ if(d){\n var time = dayjs(d);\n if(!time.isValid()) throw( \"invalid date\" );\n if(opt.snapDay) time = (from) ? time.startOf('day') : time.endOf('day');\n return time.utc().format();\n } else {\n return '*';\n }\n }\n };\n if(! (opt.format in formatter) ) throw \"invalid format\";\n var range = \"[\" + formatter[opt.format](from, true) + \" TO \" + formatter[opt.format](to) + \"]\";\n return range;\n };\n\n $scope.doAdvancedSearch = function () {\n\n var limits = {};\n\n if ($scope.limits.branch.length) {\n var field = ($scope.limits.available) ? 'on-shelf-at' : 'owned-by';\n limits[field] = $scope.limits.branch.join(' OR ');\n }\n jQuery.each(['itemtype', 'collection', 'ln', 'location', 'eformat'], function (i, field) {\n if ($scope.limits[field].length) {\n limits[field] = '\"' + $scope.limits[field].join('\" OR \"') + '\"'; // FIXME: quotes in codes will break this.\n }\n });\n if ($scope.musicSearch.toString()) {\n limits.fq = $scope.musicSearch.toString();\n }\n\n if(userService.is_staff && ($scope.limits.suppress=='0' || $scope.limits.suppress == '')){\n $scope.limits.suppress = '[0 TO 1]';\n }\n\n // These Must be single-valued.\n jQuery.each(['content', 'audience', 'suppress'], function (i, field) {\n if ($scope.limits[field]) {\n limits[field] = $scope.limits[field];\n }\n });\n\n // Format may have multiple terms\n jQuery.each(['format'], function (i, field) {\n if ($scope.limits[field]) {\n limits[field] = '\"'+$scope.limits[field]+'\"';\n }\n });\n\n if ($scope.limits.pubyear.from || $scope.limits.pubyear.to) {\n limits['pubyear'] = getRange($scope.limits.pubyear.from, $scope.limits.pubyear.to);\n }\n \n if ($scope.limits.geoscale.from || $scope.limits.geoscale.to) {\n limits['geo-scale'] = getRange($scope.limits.geoscale.from, $scope.limits.geoscale.to);\n }\n if ($scope.limits.lexile_i.from || $scope.limits.lexile_i.to) {\n limits['lexile_i'] = getRange($scope.limits.lexile_i.from, $scope.limits.lexile_i.to);\n }\n try{\n if ($scope.limits.rangeSearch.loadedDate1 || $scope.limits.rangeSearch.loadedDate2) {\n limits['load-date'] = getRange($scope.limits.rangeSearch.loadedDate1,\n $scope.limits.rangeSearch.loadedDate2, {format: 'datetime', snapDay: true} );\n }\n if ($scope.limits.rangeSearch.createdDate1 || $scope.limits.rangeSearch.createdDate2) {\n limits['create-date'] = getRange($scope.limits.rangeSearch.createdDate1,\n $scope.limits.rangeSearch.createdDate2, {format: 'datetime', snapDay: true} );\n }\n } catch (e){\n alert(e);\n return;\n }\n\n var searchParams = {\n q: kwLuceneParser.composeSubqueries($scope.subqueries),\n op: $scope.op,\n sort: $scope.limits.sort,\n limits: limits\n };\n var search = new SearchQuery(searchParams);\n\n $state.go('search-results.koha', search.stateParams());\n\n };\n\n }])\n\n .controller('SearchResultsCtrl', [\"$rootScope\", \"configService\", \"userService\", \"$stateParams\", \"$scope\", \"$state\", \"SearchQuery\", \"kohaSearchSvc\", \"kwLuceneParser\", \"kohaDlg\", function($rootScope, configService, userService, $stateParams,\n $scope, $state, SearchQuery, kohaSearchSvc, kwLuceneParser, kohaDlg){\n // controller for abstract state.\n\n $scope.federatedSearch = {\n ebsco: { id: 'eds', name: configService.altSearch.eds.label, hits: 0 },\n indexData: { id: 'indexdata', name: configService.altSearch.indexdata.label, hits: 0 },\n koha: { id: 'koha', name: 'Library Catalog', hits: 0 }, // FIXME: Make configurable.\n collapsed: false,\n search: null // This stores current search for child scopes, whether federated search is used or not.\n };\n\n// this state is retained when changing between child states.\n\n $scope.viewType = {\n set: function(viewType){\n $scope.viewType.active = configService.viewtype = viewType;\n $scope.viewType.parentClass = viewType;\n $scope.viewType.rowClass = viewType + ((viewType && viewType.indexOf('grid') > -1) ? ' row' : '');\n $scope.viewType.resultClass = viewType + ((viewType && viewType.indexOf('grid') > -1) ? ' col-xs-12 col-sm-6 col-md-4' : ' row');\n $scope.viewType.imgClass = viewType + ((viewType && viewType.indexOf('grid') > -1) ? ' thumbnail' : '');\n }\n };\n\n userService.whenAnyUserDetails().then(function() {\n $scope.useDefaultFilter = (userService.merged_prefs && userService.merged_prefs.use_default_filter) ? true : false;\n $scope.hasDefaultFilter = (userService.merged_prefs && userService.merged_prefs.default_query_filter) ? true : false;\n var initview = (userService.merged_prefs && userService.merged_prefs.selected_view_type) ? userService.merged_prefs.selected_view_type : (configService.viewtype || 'list-view');\n\n $scope.viewType.set(initview);\n $scope.$watch('viewType.active', function(viewType) {\n if (! viewType) { return; }\n $scope.viewType.set(viewType);\n if (userService.loggedin) {\n var currentSelection = userService.merged_prefs.selected_view_type;\n if (!currentSelection || currentSelection != viewType) {\n userService.addPrefs({selected_view_type: viewType});\n }\n }\n });\n });\n\n $scope.federatedSearch.go = function(childstate){\n\n resetSource(childstate);\n if(childstate=='koha'){\n var params;\n// FIXME: try to translate between limits when possible.\n\n if($scope.federatedSearch.search){\n params = $scope.federatedSearch.search.stateParams();\n } else {\n\n var newSearch = new SearchQuery({ q: $stateParams.query }); // fixme: not working.\n params = newSearch.stateParams();\n }\n $state.go( 'search-results.koha', params, { inherit: false });\n } else {\n var parsed_query = kwLuceneParser.parse(this.search.q);\n var rawSearchTerms = kwLuceneParser.extractTerms(parsed_query).filter(\n function(term){ return term.length > 2; });\n $state.go( 'search-results.' + childstate, { query: rawSearchTerms }, {inherit: false});\n }\n };\n\n var namemap = { ebsco: 'eds', indexData: 'indexdata', koha: 'koha' };\n function resetSource (src){\n $scope.federatedSearch.otherSources = [];\n ['koha','ebsco','indexData'].forEach(function(svc){\n var isTarget = (src) ? ( src == namemap[svc] ) : ( $state.current.name=='search-results.'+ namemap[svc]);\n if (svc === 'ebsco' && !configService.EbscoDiscoveryServices) return;\n if (svc === 'indexData' && !configService.IndexDataMKWS) return;\n\n if(isTarget){\n $scope.federatedSearch.currentSource = $scope.federatedSearch[svc];\n } else {\n if(svc=='koha' || configService.altSearch[namemap[svc]].on){\n $scope.federatedSearch.otherSources.push($scope.federatedSearch[svc]);\n }\n }\n });\n }\n resetSource();\n\n $scope.showSearchSummary = true;\n if (configService.opacConfig.mastheadSearchConfig && configService.opacConfig.mastheadSearchConfig.simpleSearch.hideSummary) {\n // FIXME: for anon users, assumes initial masthead mode is simple. \n if ((userService.merged_prefs && userService.merged_prefs.masthead_search_form == 'simple') || ! userService.loggedin) {\n $scope.showSearchSummary = false;\n }\n }\n\n $rootScope.$on('setSearchMethod', function(e,args) {\n $scope.showSearchSummary = true;\n if (configService.opacConfig.mastheadSearchConfig.simpleSearch.hideSummary && args == 'simple')\n $scope.showSearchSummary = false;\n });\n\n $scope.currentSearch = kohaSearchSvc.currentSearch;\n\n if(configService.isMobile()){\n $scope.federatedSearch.collapsed = true;\n $scope.facetsCollapsed = true;\n }\n\n $scope.openFacetHelp = function(){\n userService.whenAnyUserDetails().then(function() {\n\n kohaDlg.dialog({\n type: 'help',\n heading: 'Options for refining your search results',\n ngInclude: '/app/static/partials/help/facetHelpTmpl.html',\n close: true,\n scopevars: { advancedFacets: userService.merged_prefs.advanced_facets }\n });\n });\n }\n\n }])\n\n .component('searchQuerySummary', {\n templateUrl: '/app/static/partials/search/searchQuerySummary.html',\n bindings: {\n search: '<'\n },\n\n controller: [\"configService\", \"SearchQuery\", \"userService\", \"authoritySvc\", \"kwLuceneParser\", \"$q\", \"$state\", \"$injector\", function( configService, SearchQuery, userService, authoritySvc,\n kwLuceneParser, $q, $state, $injector ) {\n\n var ctrl = this;\n this.limitFields = [];\n\n\n this.$onInit = function(){\n var search = this.search;\n ctrl.federatedSource = ! ($state.is('search-results.koha'));\n\n userService.whenAnyUserDetails().then(function() {\n ctrl.sortOptions = (configService.opacConfig.mastheadSearchConfig.sortOptions)\n ? configService.opacConfig.mastheadSearchConfig.sortOptions : configService.sortoptions;\n ctrl.sortOptions = ctrl.sortOptions.map( function(opt){\n if (!opt.group) {\n delete opt.group;\n }\n return opt\n });\n if (userService.can({catalogue:{bib_ratings:'view'}})) {\n ctrl.sortOptions = ctrl.sortOptions.filter( function(opt){ return (opt.group != 'Patron Rating'); });\n }\n ctrl.sortField = search.sort || 'score desc';\n\n var picked = ctrl.sortOptions.filter( function(opt){ return opt.value==search.sort; });\n ctrl.sortLabel = (picked.length) ? picked[0].label : '?';\n // fixme: this should be uib-dropdown.\n });\n ctrl.subqueries = kwLuceneParser.extractSubqueries(search.q).subqueries;\n\n angular.forEach(ctrl.subqueries, function(sq){\n sq.q_display = sq.q;\n if (sq.field && sq.field.match(/linked_rcn|_rcn_facet$/)) {\n sq.field_display = 'Heading';\n var rcn = sq.q.replace(/\\\\([() ])/g, '$1').replace(/^\"|\"$|\\|\\|.*$/g, '');\n authoritySvc.get(rcn).then(function(r){\n sq.q_display = r.summary.heading;\n });\n } else if (sq.field === '*' && sq.q === '*') {\n sq.field_display = null;\n sq.q_display = 'All Records';\n } else {\n sq.field_display = sq.field;\n }\n });\n\n this.config = configService;\n\n angular.forEach(search.limitfields, function(vals, key) {\n\n // TODO: translate facets like 'url: *', '*:*', \n // and range limits.\n // FIXME: kwLuceneParser fails to parse date math ranges, e.g. catdate:[NOW-800DAYS TO *] excepts.\n var limfield;\n if (key == 'fq') {\n limfield = {field: key};\n limfield.fieldDisplay = key;\n limfield.values = [];\n angular.forEach(vals, function(val) {\n var limval = {value: val};\n var vmatch = val.match(/^([^:]+):(.+)$/);\n if (vmatch && vmatch.length > 2) {\n var facetName = vmatch[1];\n var facetValue = vmatch[2];\n var facetInfo = configService.searchFacetInfo[facetName];\n\n facetName = (facetInfo) ? facetInfo.display : facetName;\n facetValue = (facetInfo && facetInfo.authval) ? configService.display(facetValue, facetInfo.authval) : facetValue;\n limval.valueDisplay = facetName + \":\" + facetValue;\n }\n else {\n limval.valueDisplay = val;\n }\n limfield.values.push(limval);\n });\n ctrl.limitFields.push(limfield);\n }\n else {\n var facetInfo = configService.searchFacetInfo[key];\n var otherAuthvals = {\n ln : 'Language'\n };\n limfield = {field: key};\n limfield.fieldDisplay = (facetInfo) ? facetInfo.display :\n otherAuthvals[key] ? otherAuthvals[key] : key;\n// FIXME:\n// Relying on facetInfo means if the user doesn't have the facet defined, we don't get interpolation.\n// authval fields may also come from advanced search.\n\n\n limfield.values = [];\n angular.forEach(vals, function(val) {\n var limval = {value: val};\n var vmatch = val.match(/^\\\"(.+)\\\"$/);\n\n if (vmatch && vmatch.length > 1 && facetInfo && facetInfo.authval) {\n var authvaldisplay = configService.display(vmatch[1], facetInfo.authval);\n limval.valueDisplay = (authvaldisplay==vmatch[1]) ? val : authvaldisplay;\n } else if ( otherAuthvals[key] && vmatch && vmatch.length > 1 ) {\n var avdisp = configService.display(vmatch[1], otherAuthvals[key]);\n limval.valueDisplay = (avdisp==vmatch[1]) ? val : avdisp;\n } else if (facetInfo && facetInfo.authval === 'rcn-heading') {\n vmatch = val.match(/^\\((.+)\\)$/);\n if (vmatch && vmatch.length > 1) {\n var expr = vmatch[1];\n var rcns = [], operators = [];\n // Just in case something goes amuck\n for (var i=0; i<1000; i++) {\n var rcnMatch = expr.match(/^(\\\\\\([^)]+\\)\\d+[^\\s]+)\\s+(OR|AND|NOT)\\s+/);\n if (!rcnMatch || rcnMatch.length<3) {\n break;\n }\n rcns.push(rcnMatch[1]);\n operators.push(rcnMatch[2]);\n expr = expr.replace(/^\\\\\\([^)]+\\)\\d+[^\\s]+\\s+(OR|AND|NOT)\\s+/,'');\n }\n rcns.push(expr);\n var promises = [];\n rcns.forEach(function(token) {\n var rcn = token.replace(/\\\\([() ])/g, '$1').replace(/^\"|\"$|\\|\\|.*$/g, '');\n promises.push(authoritySvc.get(rcn));\n });\n $q.all(promises).then(function(rv) {\n var newTokens = [];\n while (rv.length > 0) {\n var r = rv.shift();\n newTokens.push(r.summary.heading);\n if (operators.length > 0) {\n var op = operators.shift();\n newTokens.push(' ' + op + ' ');\n }\n }\n limval.valueDisplay = newTokens.join('');\n });\n\n\n }\n else {\n var rcn = val.replace(/\\\\([() ])/g, '$1').replace(/^\"|\"$|\\|\\|.*$/g, '');\n limval.valueDisplay = rcn;\n authoritySvc.get(rcn).then(function(r){\n limval.valueDisplay = r.summary.heading;\n });\n }\n }\n else {\n limval.valueDisplay = val.replace(/^\\\"|\\\"$/g, '');\n }\n limfield.values.push(limval);\n });\n ctrl.limitFields.push(limfield);\n }\n });\n if(search.op && search.op=='OR'){\n ctrl.limitFields.push(\n { field: 'q.op',\n values: [ { value: 'OP', valueDisplay: 'Any term' }]\n });\n }\n\n }\n\n\n this.rmLimit = function (field, val) {\n // remove a limit and trigger new search.\n\n var query = new SearchQuery( ctrl.search );\n if(field=='q.op')\n delete query.op;\n else\n query.rmLimit(field,val);\n\n if(configService.geospatialSearch && (field == 'geo-shape') && ARCHVIEW.mapLayers && ARCHVIEW.mapLayers.length > 0){\n var mapComptrollerSvc = $injector.get('mapComptrollerSvc');\n mapComptrollerSvc.clearAOIAndDontSearch();\n }\n\n $state.go('search-results.koha', query.stateParams(), { inherit: false });\n // FIXME: ^^^Will have to distinguish btwn child states\n };\n\n this.rmQuery = function (i) {\n\n var subq = angular.copy(this.subqueries);\n subq.splice(i,1);\n\n var q = kwLuceneParser.composeSubqueries(subq);\n\n var searchParams = {\n q: q,\n op: ctrl.search.op,\n sort: ctrl.search.sort,\n limits: ctrl.search.limitfields\n };\n\n var newSearch = new SearchQuery(searchParams);\n\n $state.go('search-results.koha', newSearch.stateParams());\n\n };\n\n this.reSort = function(sortfield){\n var newSearch = new SearchQuery( this.search );\n newSearch.sort = sortfield;\n newSearch.go();\n }\n\n }]\n\n })\n\n .controller('KohaSearchSummaryCtrl', [\"$scope\", \"$q\", \"$filter\", \"configService\", \"userService\", \"cartService\", \"bibService\", \"acqListSearchAPI\", \"BibBatchService\", \"awDailyExportService\", \"kohaDlg\", \"alertService\", \"$location\", \"$uibModal\",\n function($scope, $q, $filter, configService, userService, cartService, bibService,acqListSearchAPI, BibBatchService, awDailyExportService, kohaDlg, alertService, $location, $uibModal ){\n\n $scope.changePagesize = function(newSize){\n // this and pager should both be in parent controller,\n // and SearchQuery object should probably know its target (eg koha or ebsco)\n if(newSize != $scope.numPerPage){\n // Note that we mutate configservice's syspref val to make it persistent.\n // numResults should really be configured within wack.\n $scope.search.numPerPage = configService.OPACnumSearchResults = newSize;\n $scope.search.page = 1;\n $scope.search.fetch();\n }\n };\n\n $scope.$watch('federatedSearch.search.results',\n function(results){\n $scope.search = $scope.federatedSearch.search;\n if(results) $scope.numPerPage = results.bibs.length;\n }\n );\n\n\n var selectedBibList = function(){\n return Object.keys($scope.search.results.selectedBibs);\n };\n\n var targetedActions = [\n { name: 'cart',\n icon: 'bi bi-bag-check',\n text: 'Add to ' + configService.wording.cart,\n can: function(){ return configService.opacbookbag; },\n do: function(){\n selectedBibList().forEach(function(bibid){ cartService.add(bibid);});\n }\n },\n { name: 'list',\n icon: 'bi bi-bookshelf',\n text: 'Add to ' + configService.wording.list,\n can: function(){ return configService.virtualshelves; },\n do: function(){\n kohaDlg.addToList( selectedBibList() );\n }\n },\n { name: 'acqlist',\n icon: 'bi bi-bookshelf',\n text: 'Add to ' + configService.AcqList.wording.addToList,\n can: function(){\n return configService.AcqLists &&\n userService.can({editcatalogue: {item_list: 'update'}});\n },\n do: function(){\n var bibids = selectedBibList();\n var bib_promises = bibids.map(function(bibid){ return bibService.get(bibid); });\n $q.all(bib_promises).then(function(bib_results) {\n var types = {};\n angular.forEach(bib_results, function(bib) {\n console.dir(bib.summary.order_types);\n angular.forEach(bib.summary.order_types, function(val, otype) {\n angular.forEach(val, function(val2, osub) {\n types[otype + ' ' + osub] = true;\n });\n });\n });\n acqListSearchAPI.addBibsToList(bibids, types);\n });\n }\n },\n { name: 'hold',\n icon: 'bi bi-chevron-double-down',\n text: $filter('ucfirst')(configService.wording.placehold),\n can: function(){ return configService.RequestOnOpac; },\n do: function(){\n if($scope.selectedCount()){\n kohaDlg.placeHold(selectedBibList());\n }\n }\n },\n ];\n\n $scope.canAddToCart = configService.opacbookbag;\n\n $scope.addSelectedToCart = function(){\n if($scope.selectedCount()){\n selectedBibList().forEach(function(bibid){ cartService.add(bibid);});\n }\n }\n\n $scope.placeSelectedHolds = function(){\n if($scope.selectedCount()){\n kohaDlg.placeHold(selectedBibList());\n }\n }\n\n $scope.addSelectedToList = function(){\n if($scope.selectedCount()){\n kohaDlg.addToList( selectedBibList() );\n }\n }\n\n $scope.targetedActions = [];\n var setTargetedActions = function(){ // actions on selected results.\n $scope.targetedActions.length = 0;\n if($scope.selectedCount()){\n targetedActions.forEach(function(action){\n if(action.can()) $scope.targetedActions.push(action);\n });\n }\n };\n\n// FIXME:\n// SCOPE.SEAR4CH MAY BE GONE (ON BACK BUTTON ) ?\n\n $scope.selectedCount = function(){\n if(!($scope.search||{}).results) return;\n var cnt = Object.keys($scope.search.results.selectedBibs).length;\n return (cnt) ? cnt : undefined;\n };\n\n $scope.selectAll = function (bool) {\n if (bool) {\n $scope.search.results.setSelected();\n } else {\n $scope.search.results.clearSelected();\n }\n };\n\n $scope.$watch('search.results.selectedBibs', function(bibs){\n if(!bibs) return;\n setTargetedActions();\n }, true);\n\n\n $scope.exportSearch = function() {\n var deferred = $q.defer();\n var vendors = [];\n angular.forEach($scope.search.results.facets, function(facet) {\n if (facet.field == 'homebranch') {\n angular.forEach(facet.values, function(val) {\n vendors.push(val.value);\n });\n }\n });\n if (vendors.length) {\n deferred.resolve(1);\n }\n else if ($scope.search.results.bibs && $scope.search.results.bibs.length) {\n console.dir($scope);\n var bibid = $scope.search.results.bibs[0].id;\n bibService.get(bibid).then(function(bib) {\n var items = bib.summary.available_at;\n if (items.length) {\n vendors.push(items[0].branch);\n deferred.resolve(1);\n }\n else {\n deferred.reject();\n }\n });\n }\n else {\n deferred.reject();\n }\n\n deferred.promise.then(function() {\n acqListSearchAPI.exportSearch({vendors: vendors, query: $location.url()});\n }, function() {\n alertService.add({msg: 'No hits in this search!'});\n });\n };\n\n $scope.saveSearch = function () {\n\n userService.whenAuthenticatedUserDetails().then(function(d){\n // if (userService.savedSearches.has($scope.search)) {\n // alertService.add({\n // msg: 'This search has already been saved.'\n // });\n // return;\n // }\n\n $uibModal.open({\n backdrop: 'static',\n templateUrl: '/app/static/partials/savedSearchConfirm-modal.html',\n resolve: {\n defaultName: function() {\n return userService.savedSearches.defaultName($scope.search);\n },\n search: function() {\n return $scope.search;\n }\n },\n controller: [\"$scope\", \"defaultName\", \"search\", function($scope, defaultName, search) {\n $scope.search = search;\n $scope.name = defaultName;\n $scope.error = false;\n $scope.doOverwrite = 0;\n $scope.save = function(rv) {\n userService.savedSearches.add($scope.search, $scope.name, JSON.stringify(userService.getLastExecutedSearch()), $scope.doOverwrite).then(function (rv) {\n if (rv == 1) {\n $scope.error = false;\n $scope.$close(1);\n }\n else if (rv == 0) {\n $scope.error = false;\n $scope.doOverwrite = 1;\n }\n else {\n $scope.error = true;\n }\n });\n };\n }],\n }).result.then(function(rv) {\n if (rv == 1) {\n alertService.add({\n msg: 'Search saved!'\n });\n }\n });\n });\n };\n\n $scope.addToAcqListAll = function() {\n acqListSearchAPI.addQueryHitsToList($scope.search);\n };\n\n $scope.sendResults = function () {\n if (!$scope.search.results.hits) return;\n\n userService.whenAuthenticatedUserDetails().then(function(details){\n var userdata = details;\n\n if(userdata.email){\n kohaDlg.dialog({\n heading: \"Email Search Results\",\n message: \"Please enter the recipient email address.\",\n inputs: [{\n name: 'recipient',\n label: 'Recipient email',\n val: userdata.email,\n type: 'email'\n },{\n name: 'subject',\n label: 'Subject',\n val: \"Your search results\"\n }],\n buttons: [{\n val: true,\n label: 'Send',\n btnClass: \"btn-primary\",\n submit: true\n }, {\n val: false,\n label: 'Cancel'\n }]\n }).result.then(function (modalresult) {\n if (modalresult) {\n var options = {\n op: 'email',\n recipient: modalresult.recipient,\n subject: modalresult.subject,\n };\n\n BibBatchService.submit( $scope.search.asUrl().split('/').reverse()[0] , options);\n }\n });\n } else {\n kohaDlg.dialog({\n type: \"notify\",\n alertClass: \"warning\",\n heading: \"No email address on file\",\n message: \"You must have an email address on file with the library to send email.\"\n });\n }\n });\n };\n $scope.renderResults = function(dload){\n var options = {\n op: (dload) ? 'download' : 'print'\n };\n BibBatchService.submit( $scope.search.asUrl().split('/').reverse()[0], options);\n };\n\n $scope.prepareDaily = function(){\n awDailyExportService.startModal($scope.search);\n };\n\n }])\n\n .controller('KohaFacetViewCtrl', [\"$scope\", \"$uibModal\", \"configService\", \"userService\", function($scope, $uibModal, configService, userService){\n\n $scope.facets = [];\n\n $scope.advancedFacets = false;\n userService.whenAnyUserDetails().then(function() {\n $scope.advancedFacets = userService.merged_prefs.advanced_facets;\n });\n\n $scope.$watch('currentSearch()', function(srch){\n if(srch){\n srch.whenResults().then(function(results){\n\n var newFacetsArr = angular.copy(results.facets);\n var newFacetsObj = {};\n var merging = ( $scope.facets.length );\n\n if(merging){\n // merge facets.\n newFacetsObj = newFacetsArr.reduce(function(obj,item){\n obj[item.field] = item;\n return obj;\n }, {});\n\n for (var i = $scope.facets.length - 1; i >= 0; i--) {\n var extantFacet = $scope.facets[i];\n\n if(newFacetsObj[extantFacet.field]){\n // todo: can't animate replacement here,\n // this block allows animation of parent facet field,\n // not individual facet values. will have to merge\n // the values array to animate that.\n extantFacet.values = newFacetsObj[extantFacet.field].values;\n delete newFacetsObj[extantFacet.field];\n } else {\n // remove.\n $scope.facets.splice(i,1);\n }\n }\n }\n\n newFacetsArr.forEach(function(facetfield){\n if( !merging || newFacetsObj[facetfield.field] ){\n if(configService.isMobile()) facetfield.collapsed = true;\n facetfield.operator = 'OR';\n $scope.facets.push(facetfield);\n }\n });\n\n });\n }\n\n });\n\n $scope.facetDlgOpen = function (field) {\n $uibModal.open({\n backdrop: false,\n templateUrl: '/app/static/partials/facet-modal.html',\n controller: 'FacetDlgCtrl',\n windowClass: \"modal facet-viewer\",\n resolve: {\n facetfield: function () {\n return field;\n }\n }\n });\n return false;\n };\n\n $scope.toggleFacetOperator = function(f) {\n f.operator = (f.operator == 'OR') ? 'AND' : 'OR';\n };\n\n }])\n\n .controller('KohaSearchResultsCtrl', [\"$http\", \"$scope\", \"$rootScope\", \"$state\", \"$timeout\", \"kohaSearchSvc\", \"SearchQuery\", \"configService\", \"$anchorScroll\", \"userService\", \"ratingService\", \"bibService\", \"ebscoService\", \"indexDataApi\", function ($http, $scope,$rootScope, $state, $timeout,\n kohaSearchSvc, SearchQuery, configService, $anchorScroll, userService, \n ratingService, bibService, ebscoService, indexDataApi) {\n\n $scope.$watch('useDefaultFilter', function(newVal,oldVal) {\n if ((typeof(oldVal) == 'boolean') && (oldVal !== newVal)) {\n userService.addPrefs({use_default_filter: newVal}).then(function() {\n triggerSearch();\n });\n }\n });\n\n $scope.resultsSelection = {};\n // FIXME: federatedSearch.search from parent controller\n $scope.updateSelection = function(){\n $scope.federatedSearch.search.results.clearSelected();\n for(var bibid in $scope.resultsSelection){\n if($scope.resultsSelection[bibid]) $scope.federatedSearch.search.results.setSelected(bibid);\n }\n };\n $scope.$watch('federatedSearch.search.results.selectedBibs',function(bibs){\n if(bibs){\n $scope.resultsSelection = angular.copy(bibs);\n }\n }, true);\n\n var newSearch = new SearchQuery({fromState: true});\n var lastSearch = kohaSearchSvc.currentSearch();\n\n var doFetch = false;\n if(newSearch.equals(lastSearch)){\n $scope.search = $scope.federatedSearch.search = lastSearch;\n } else {\n $scope.search = newSearch;\n doFetch = true;\n }\n\n var triggerSearch = function(search) {\n console.log('RELOADING SEARCH');\n\n if(!search) search = $scope.search;\n $rootScope.$broadcast('searchStart');\n// ... we shouldn't have to set federatedSearch here\n\n kohaSearchSvc.currentSearch(search);\n\n $scope.federatedSearch.search = search;\n\n search.fetch().then(function searchSuccess( results ) {\n $rootScope.$broadcast('searchComplete', {results: results.bibs }); // This appears still necessary to unblock map.\n $scope.federatedSearch.koha.hits = results.hits;\n if (configService.search.singleResultShortcut && (results.bibs.length === 1 && results.hits == 1)) {\n var bib = results.bibs[0];\n userService.whenAnyUserDetails().then(function() {\n if (userService.is_staff) {\n if(/^\\d+$/.test(search.q)){\n // FIXME: Don't load holdings twice.\n bibService.holdings(bib.id).then(function(holdings) {\n var re = new RegExp(search.q)\n var item = holdings.items.find(function(item){ return item.barcode && item.barcode.match(re); });\n if (item)\n $state.go('staff.bib.items_status', {itemid: item.itemnumber, biblionumber: bib.id}, {reload: true});\n else\n $state.go('staff.bib.details', {biblionumber: bib.id}, {reload: true});\n });\n } else {\n $state.go('staff.bib.details', {biblionumber: bib.id}, {reload: true});\n }\n } else {\n $state.go('work', {bibid: bib.id}, {reload: true});\n }\n });\n }\n $timeout(function(){\n $anchorScroll(); // This doesn't seem to work; see PT 63563124\n },10);\n }, function searchFail(){\n $rootScope.$broadcast('searchFailed');\n });\n\n // federated search results.\n // we do this just in the BV results since query terms can't be changed in\n // either of the federated search results interfaces.\n if($scope.config.altSearch.eds && $scope.config.altSearch.eds.on){\n // note there's no attempt to map local search fields to third-party fields.\n\n var rawSearchTerms = search.rawTerms();\n var searchparams = {\n resultsperpage: $scope.config.altSearch.eds.injectNum,\n includefacets: 'n',\n \"query-1\": rawSearchTerms\n };\n // EDS apparently can't handle request for 0 results.\n if(searchparams.resultsperpage < 1 ) searchparams.resultsperpage = 1;\n if(rawSearchTerms.length){\n\n $http.get('/api/eds/search', { params: searchparams, cache: true }).then(function(rsp){\n $scope.federatedSearch.ebsco.hits = rsp.data.SearchResult.Statistics.TotalHits;\n $scope.federatedSearch.ebsco.injectResults = ($scope.config.altSearch.eds.injectNum > 0 ) ?\n rsp.data.SearchResult.Data.Records : [];\n $scope.federatedSearch.ebsco.injectResults.forEach(function(bib){\n search.whenResults().then(function(bvResults){\n var offset = Math.min( bvResults.hits, configService.altSearch.eds.injectOffset );\n bib.ResultId += offset;\n })\n // title is always first 'Items' element.\n bib.Items.forEach(function(meta){\n meta.Data = ebscoService.xformItem(meta.Data);\n });\n var title = bib.Items.shift();\n bib._title = title ? title.Data : ( bib.Header.PubType + ' | ' + bib.Header.DbLabel );\n });\n ebscoService.query = ebscoService.queryStringToHash(rsp.data.SearchRequestGet.QueryString);\n });\n }\n }\n if($scope.config.altSearch.indexdata && $scope.config.altSearch.indexdata.on){\n if(indexDataApi.runIndexDataSearch) {\n indexDataApi.runIndexDataSearch(search.q);\n }\n $rootScope.$on('indexDataRecordsReady', function (n, args) {\n $scope.federatedSearch.indexData.hits =\n $scope.federatedSearch.indexData.indexdata_totalhits = args.merged;\n });\n }\n\n };\n\n if(doFetch) triggerSearch();\n\n $scope.patronRatings = [];\n $scope.reloadRatings = function() {\n return ratingService.getPatronRatings().then(function(ratings) {\n $scope.patronRatings = ratings;\n });\n };\n\n }])\n .controller('BibSummaryCtrl', [\"$scope\", \"bibService\", \"configService\", function ($scope, bibService, configService) {\n\n // Hack. Ensures that a bib is in scope, given a bibid.\n // Assumes in ng-repeat, so no watch.\n $scope.config = configService;\n if($scope.bibid){\n bibService.get($scope.bibid).then(function(bib){\n $scope.bib = bib;\n });\n }\n // TODO: REMOVE ME.\n\n }])\n\n .controller('BibDetailCtrl', [\"$scope\", \"$window\", \"$rootScope\", \"$uibModal\", \"$http\", \"$sce\", \"$timeout\", \"$injector\", \"kohaDlg\", \"bib\", \"holdings\", \"kohaTagsSvc\", \"kohaXISBNSvc\", \"kwNoveListSvc\", \"configService\", \"userService\", \"ratingService\", \"cartService\", \"$state\", \"kohaSearchSvc\", \"acqListSearchAPI\", \"$stateParams\", \"kwApi\", \"bvStrTmplParse\", \"$filter\", function ($scope, $window, $rootScope, $uibModal, $http, $sce, $timeout,\n $injector, kohaDlg, bib, holdings, kohaTagsSvc, kohaXISBNSvc, kwNoveListSvc,\n configService, userService, ratingService,cartService, $state, kohaSearchSvc, acqListSearchAPI,\n $stateParams, kwApi, bvStrTmplParse, $filter) {\n $scope.user = userService;\n\n $scope.search = kohaSearchSvc.currentSearch();\n\n $scope.showIncrement =5;\n $scope.showLimit = 5;\n $scope.showMore = false;\n\n $scope.setShowAll = function(){\n $scope.showLimit = bib.records.length;\n }\n $scope.invalidHash = {}\n $scope.setShowReset = function(what) {\n for ( var recIndex = 0; recIndex < bib.records.length; recIndex++ ) {\n var record = bib.records[recIndex];\n record.failed = null;\n }\n }\n\n $scope.updateLimit = function (much) {\n $scope.showLimit += much;\n }\n\n $scope.setLimit = function () {\n $scope.showLimit = $scope.showIncrement;\n }\n $scope.addToListDlgOpen = kohaDlg.addToList;\n\n $scope.processUUID = function(bib){\n $scope.processingUUIDs = true;\n $scope.showInvalid = [];\n $scope.validRecords = 0;\n if( !bib.records || bib.records.length == 0)\n return;\n var flatUUID = [];\n for(var bIndex = 0; bIndex < bib.records.length;bIndex++){\n var currGUIDE = bib.records[bIndex].GUIDE;\n var currUUID = bib.records[bIndex].uuid;\n if(currGUIDE)\n flatUUID.push(currGUIDE)\n else if(currUUID)\n flatUUID.push(currUUID)\n }\n\n /*ARCHVIEW.validateOtherDomain(flatUUID).done(function(value){\n $scope.showInvalid = value;\n }); */\n\n var UUIDS = [];\n var DLSOViewerService = $injector.get('DLSOViewerService');\n for ( var recIndex = 0; recIndex < bib.records.length; recIndex++ ) {\n var record = bib.records[recIndex];\n var GUIDE = record.GUIDE;\n var newRec = DLSOViewerService.extractMarc(record,bib);\n record.GUIDE = GUIDE;\n UUIDS.push(record.uuid);\n }\n $scope.remaining = UUIDS.length;\n function successCB(index, resolve) {\n return function (d) {\n var currRecord = bib.records[index];\n currRecord.failed = false;\n\n var metadata = ARCHVIEW.metadataConversion(d.data);\n currRecord.permitted = (metadata._acl_permitted === 1 || metadata._acl_permitted === \"1\") ? true : false;\n delete metadata._acl_permitted;\n currRecord.metadata = d.data;\n currRecord.thumbnail = '/api/dlso/' + currRecord.uuid + '/thumbnail';\n currRecord.checked = false;\n\n if (currRecord.linktext) {\n currRecord._title = currRecord.linktext;\n if(currRecord.size || metadata.product_size)\n currRecord._title += (\" - \" + $filter('filesize')(currRecord.size || metadata.product_size));\n } else {\n var tmpl = new bvStrTmplParse(configService.dls.fileTmpl);\n currRecord._title = tmpl.render(metadata) || currRecord.product_type;\n }\n if(!currRecord.summary){\n kwApi.Dlso.summary( { id: currRecord.uuid } , (rsp) => {\n console.log(rsp);\n if(rsp.summary){\n currRecord.summary = rsp.summary;\n }\n })\n }\n\n currRecord.tooltip = currRecord.filename || metadata.title;\n $scope.validRecords++;\n resolve(true);\n };\n }\n $scope.invalidHash = {};\n\n var tempUUIDS = UUIDS.slice();\n\n $scope.maxResults = $scope.showLimit;\n\n var queryMetadata = function(index){\n return new Promise( \n function(resolve, reject)\n { \n return $http.get('/api/dlso/' + tempUUIDS[index] + '/metadata', {cache: true}).then(successCB(index,resolve), \n \n function () {\n bib.records[index].failed = true;\n \n var currGUIDE = bib.records[index].GUIDE && bib.records[index].GUIDE.length == 49 ? bib.records[index].GUIDE.substr(8,4) : null;\n \n if(currGUIDE){\n $scope.invalidHash[currGUIDE] = $scope.invalidHash[currGUIDE] == null ? 1 : $scope.invalidHash[currGUIDE] += 1;\n }\n \n resolve(true);\n });\n }\n );\n }; \n \n var loopDLSO = function(index){\n return queryMetadata(index).then(function(result){\n {\n $scope.remaining--;\n if(tempUUIDS.length > index && $scope.maxResults > $scope.validRecords && $scope.remaining > 0){\n console.log(tempUUIDS[index])\n return loopDLSO(index + 1)\n }\n else {\n $scope.processingUUIDs = false;\n }\n }\n \n })\n }\n \n return loopDLSO(0).then(function(){\n \n if(ARCHVIEW && ARCHVIEW.domainFallback && ARCHVIEW.domainFallback.allDomains ){\n for(var i in $scope.invalidHash){\n var count = $scope.invalidHash[i];\n var domain = ARCHVIEW.domainFallback.allDomains[i*1];\n if(domain == null)\n $scope.showInvalid.push(count + \" Digital Object(s) from Unknown Domain\");\n else\n $scope.showInvalid.push(domain);\n }\n }\n //force apply\n $timeout(function(){\n $scope.$apply();\n },50);\n })\n\n /*var promises = tempUUIDS.map( function(uuid, index) {\n return $http.get('/api/dlso/' + uuid + '/metadata', {cache: true}).then(successCB(index), errorCB(index));\n });\n\n return $q.all(promises);*/\n };\n\n $scope.bib = bib; // this is actual bib object, not promise due to resolve on route.\n\n $scope.configService = configService;\n $scope.useCatSources = !!configService.catsources;\n if (configService.geospatialSearch && ARCHVIEW.mapLayers && ARCHVIEW.mapLayers.length > 0) {\n var mapComptrollerSvc = $injector.get('mapComptrollerSvc');\n mapComptrollerSvc.pullClassification(bib);\n }\n if((configService.geospatialSearch || configService.dls.enabled) && bib.marcXML == null){\n $.ajax({\n type: \"GET\",\n url: ARCHVIEW.MarcXMLExport.format(bib.id) ,\n cache: false,\n processData : false,\n contentType: \"text/html\",\n dataType : \"text\",\n headers: configService.getXSRFHeader(),\n success: function (xml) {\n \n bib.marcXML = xml;\n var groupy = [bib];\n if(configService.dls.enabled)\n $scope.processUUID(bib);\n if (configService.geospatialSearch && ARCHVIEW.mapLayers && ARCHVIEW.mapLayers.length > 0) {\n mapComptrollerSvc.mapCast('fillMap', {\n results: groupy\n })\n }\n },\n error: function (err) {\nconsole.warn(err);\n }\n });\n }else{\n var groupy = [bib];\n if(configService.dls.enabled)\n $scope.processUUID(bib);\n if (configService.geospatialSearch && ARCHVIEW.mapLayers && ARCHVIEW.mapLayers.length > 0) {\n mapComptrollerSvc.mapCast('fillMap', {\n results: groupy\n })\n }\n }\n\n var completeRecordProcessing = function(bib,data,xml){\n bib.marcXml = xml;\n var bibby = data;\n data.records = $.map(data.uuids,function(uuid){ return { uuid : uuid}; }),\n $scope.processUUID(bibby);\n $scope.bib.records = bibby.records;\n //send message to map system to update list\n\n if (configService.geospatialSearch && ARCHVIEW.mapLayers && ARCHVIEW.mapLayers.length > 0) {\n var mapComptrollerSvc = $injector.get('mapComptrollerSvc');\n mapComptrollerSvc.mapCast('updateBibData', bibby.records);\n }\n };\n $scope.addDLSOToCart = function(){\n var triggerFailed = false;\n for(var recIndex = 0; recIndex < bib.records.length;recIndex++){\n var record = bib.records[recIndex];\n if(record.checked){\n if(record.failed){\n triggerFailed = true;\n } else {\n var text = record._title;\n if(!text){\n var metadata = ARCHVIEW.metadataConversion(record.metadata);\n var tmpl = new bvStrTmplParse(configService.dls.fileTmpl);\n text = tmpl.render(record.metadata) || record.tooltip;\n }\n cartService.addDLSO({\n text : text,\n bibTitle: bib.title,\n uuid : record.uuid,\n thumbnail : record.thumbnail,\n marcXML : bib.marcXML,\n marcID : bib.id\n });\n }\n }\n }\n if(triggerFailed)\n alert(\"Digital Documents that are not in the repository will not be added to the cart\");\n\n };\n\n $scope.selectAllDLSOs = function(bool){\n for(var recIndex = 0; recIndex < bib.records.length; recIndex++){\n var record = bib.records[recIndex];\n if(bool && record.permitted){\n record.checked = true;\n } else {\n record.checked = false;\n }\n }\n };\n\n userService.whenAnyUserDetails().then(function(){\n $scope.updateButtons();\n });\n $scope.$on('loggedin', function(e, args) { $scope.updateButtons(); });\n $scope.$on('loggedout', function(e, args) { $scope.updateButtons(); });\n $scope.updateButtons = function(){\n $scope.canAttachDlso = $scope.canDeleteDlso = false;\n\n if(!configService.dls.enabled) return;\n $scope.canAttachDlso = (configService.catsources) ?\n userService.can({editcatalogue:{ dlso:'attach' } }, \"catsource=\"+bib.marc.subfield(\"942x\"))\n : userService.can({editcatalogue: { dlso : 'attach' } });\n $scope.canDeleteDlso = (configService.catsources) ?\n userService.can({editcatalogue:{ dlso:'delete' } }, \"catsource=\"+bib.marc.subfield(\"942x\"))\n : userService.can({editcatalogue: { dlso : 'delete' } });\n\n };\n $scope.showUpload = function(){\n //check BIB\n if(bib){\n var bibCatSource = bib.marc.has(\"942\") ? bib.marc.subfield(\"942x\") : null;\n if($scope.useCatSources && bibCatSource == null){\n alert(\"The current catalogue does not have an associated Catalog Source.\");\n return;\n }\n else{\n if(!userService.can({editcatalogue: { dlso : 'upload' } } , \"catsource=\"+bibCatSource)) {\n alert(\"You do not have permission to add documents to Catalogs with the \"+ bibCatSource + \" source\");\n return;\n }\n }\n }\n\n //OPEN if user has access\n var bb = $uibModal.open({\n backdrop: 'static',\n templateUrl: '/app/static/partials/productUpload-modal.html',\n controller: 'AddProductModalCtrl',\n windowClass: \"modal\",\n resolve: {\n bib: function () {\n return bib;\n }\n }\n });\n\n bb.result.then(function(){\n //Update digital Documents\n $http.get(\"/api/work/\"+bib.id).then(function(rsp){ \n //always refresh marcXML\n $.ajax({\n type: \"GET\",\n url: ARCHVIEW.MarcXMLExport.format(bib.id) ,\n cache: false,\n processData : false,\n contentType: \"text/html\",\n dataType : \"text\",\n headers: configService.getXSRFHeader(),\n success: function (xml) {\n completeRecordProcessing(bib,rsp.data,xml);\n },\n error: function (err) {\n }\n });\n })\n });\n return false;\n };\n\n $scope.showDelete = function(){\n //check BIB\n if(bib){\n var bibCatSource = bib.marc.has(\"942\") ? bib.marc.subfield(\"942x\") : null;\n if( $scope.useCatSources && bib != null && !userService.can({editcatalogue: { dlso : 'delete' } } , \"catsource=\"+bibCatSource)) {\n alert(\"You do not have permission to delete DLSOs associated with the \"+ bibCatSource + \" source\");\n return;\n }else if( bib == null && !userService.can({editcatalogue: { dlso : 'delete' } } )) {\n alert(\"You do not have permission to delete DLSOs\");\n return;\n }\n \n }\n var deletionRecords = [];\n for(var recIndex = 0; recIndex < bib.records.length;recIndex++){\n var record = bib.records[recIndex]; \n if(record.checked){\n deletionRecords.push(record);\n }\n }\n \n if(deletionRecords.length == 0){\n alert(\"Please select a DLSO to delete\");\n return;\n }\n \n //OPEN if user has access\n var bb = $uibModal.open({\n backdrop: 'static',\n templateUrl: '/app/static/partials/productDelete-modal.html',\n controller: 'DeleteProductModalCtrl',\n windowClass: \"modal\",\n resolve: {\n bib: function () {\n return bib;\n },\n deletions: function(){\n return deletionRecords;\n }\n }\n });\n\n bb.result.then(function(){\n //Update digital Documents\n $http.get(\"/api/work/\"+bib.id).then(function(rsp){ \n //always refresh marcXML\n\n $.ajax({\n type: \"GET\",\n url: ARCHVIEW.MarcXMLExport.format(bib.id) ,\n cache: false,\n processData : false,\n contentType: \"text/html\",\n dataType : \"text\",\n headers: configService.getXSRFHeader(),\n success: function (xml) {\n completeRecordProcessing(bib,rsp.data,xml);\n },\n error: function (err) {\n }\n });\n\n })\n\n });\n return false;\n\n };\n\n $scope.holdings = holdings;\n $scope.holdings.selectedItems = {};\n\n $scope.sameSelectionProgram = function () { // Determine whether items have the same selection program.\n var firstProgram = undefined,\n selectionProgram = undefined,\n theSame = true; // The same until proven otherwise.\n\n angular.forEach($scope.holdings.item, function (val, key) {\n if (val && val.fields) {\n selectionProgram = val.fields.order_type + ' ' + val.fields.program;\n }\n\n if (!firstProgram) {\n firstProgram = selectionProgram;\n }\n\n if (firstProgram !== selectionProgram) {\n theSame = false;\n }\n });\n\n return theSame; // Returns true or false.\n }\n\n $scope.addItemsToAcqList = function() {\n // Only a single type now\n var foundType;\n var itemids = [];\n var types = {};\n angular.forEach($scope.holdings.selectedItems, function(val, itemid) {\n if (val) {\n itemids.push(itemid);\n var order_type = $scope.holdings.item[itemid].fields.order_type;\n var order_subtype = $scope.holdings.item[itemid].fields.program;\n foundType = order_type;\n \n }\n });\n\n acqListSearchAPI.addItemsToList(itemids, foundType, $scope);\n $scope.holdings.selectedItems = {};\n };\n\n $scope.holdings = holdings;\n\n var check_callslip_perms = function(){\n var callslip_check = { op: 'check_place', work_id: bib.id, type: 'callslip' };\n if(configService.OPACCallslipRequests){\n $http.post('/api/callslip', $.param(callslip_check), {headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}} )\n .then(function(rsp){\n rsp.data.forEach(function(itemid){\n itemid = parseInt(itemid,10);\n $scope.holdings.item[itemid].callslip_allowed = true;\n $scope.bib.callslip_allowed = true;\n });\n });\n }\n if(configService.OPACDocumentDeliveryRequests){\n callslip_check.type = 'doc_del';\n $http.post('/api/callslip', $.param(callslip_check), {headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}} )\n .then(function(rsp){\n rsp.data.forEach(function(itemid){\n itemid = parseInt(itemid,10);\n $scope.holdings.item[itemid].doc_del_allowed = true;\n $scope.bib.doc_del_allowed = true;\n });\n });\n }\n };\n userService.whenGetUserId().then(function(){\n if(userService.loggedin){\n check_callslip_perms();\n } else {\n $scope.$on('loggedin', function(e) { check_callslip_perms(); });\n }\n });\n\n $scope.getFullDocumentURL = function(uuid){\n return \"/api/dlso/\"+uuid;\n }\n\n $scope.showDocument = function (uuid, idx) {\n var passedRecord = null;\n for(var currRec = 0; currRec < bib.records.length;currRec++){\n if(bib.records[currRec].uuid == uuid){\n passedRecord = bib.records[currRec];\n }\n }\n var DLSOViewerService = $injector.get('DLSOViewerService');\n DLSOViewerService.show(passedRecord, bib, false, idx);\n\n return false;\n };\n\n let subtitle = 'Details for ' + bib.title;\n document.title = $rootScope.pageTitle + ' | ' + subtitle;\n\n $scope.hidden = {}; // for hiding item records in a summary.\n\n var isbn = (bib.isbn && typeof bib.isbn === 'object') ? bib.isbn[0] : bib.isbn;\n\n $scope.novelistContent = {\n on: kwNoveListSvc.on,\n authorSearch: function(author){ return \"/app/search/author:(\" + encodeURIComponent(author) + \")\"; }\n };\n\n if(isbn && kwNoveListSvc.on){\n\n var youMayAlsoLike = { similarAuthors: true, similarSeries: true, similarTitles: true };\n // not actually using this -- but expect to group these three into one element.\n angular.forEach(\n kwNoveListSvc.getContent( isbn ),\n function(promise, feature){\n promise.then(function(content){\n $scope.novelistContent[feature] = content;\n if(youMayAlsoLike[feature]) $scope.novelistContent.youMayAlsoLike = true;\n });\n });\n }\n\n $scope.searchForTitleInHTML = $sce.trustAsHtml($scope.config.OPACSearchForTitleIn.replace(/TITLE/g, bib.title).replace(/AUTHOR/g, bib.marc.subfield('100a') || '').replace(/ISBN/g, isbn || ''));\n if ($scope.config.TagsShowOnDetail) {\n if (!bib.tags) {\n kohaTagsSvc.get($scope.bib.id).then(function (tags) {\n bib.tags = tags;\n });\n }\n }\n\n $http.post('/api/work/'+ $scope.bib.id + '/log', $.param({ action: 'view' }),\n {headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}} );\n\n $scope.currentTab = 'holdings';\n $scope.tabSelect = function (tabname) {\n // hack to delay rendering of tab content until user clicks on tab.\n // This is here in case anything needs to be done on tab-switch.\n // We use Separate controllers inside tabs, which watch the currentTab value.\n $scope.currentTab = tabname;\n };\n\n // Syndetics data: load immediately.\n if ($scope.config.SyndeticsEnabled) {\n $http.get('/api/work/' + $scope.bib.id + '/syndetics').then(function (rsp) {\n $scope.syndetics = rsp.data;\n });\n }\n\n // XISBNs: load immediately.\n if ($scope.config.OPACFRBRizeEditions) {\n $scope.xisbns = [];\n kohaXISBNSvc.get($scope.bib.id).then(function (editions) {\n editions.forEach(function (bib_q) {\n bib_q.then(function (bib) {\n $timeout(function () {\n $scope.xisbns.push(bib);\n }, 0);\n\n });\n });\n });\n }\n\n if(bib.isSerial && configService.OPACSerialIssueDisplayCount){\n kwApi.Periodical.getForBib({bibid: bib.id}, function(pubs){\n var hasSubs = pubs.reduce(function(acc,pub){ return acc + pub.total_subscriptions; }, 0);\n if(hasSubs) $scope.periodicals = pubs;\n });\n }\n\n $scope.staffView = function() {\n $state.go('staff.bib.details', {biblionumber: $scope.bib.id});\n };\n\n $scope.recordView = function (param) {\n // open modal viewer for marc record.\n kohaDlg.recordView($scope.bib.id, param);\n return false;\n };\n\n $scope.callslipDlgOpen = function (type) {\n if (!type) type = 'callslip';\n\n $uibModal.open({\n backdrop: false,\n templateUrl: '/app/static/partials/callslip-modal.html',\n controller: 'CallslipDlgCtrl',\n windowClass: \"modal callslip-add\",\n resolve: {\n type: function () {\n return type;\n },\n bib: function () {\n return {\n id: $scope.bib.id,\n title: $scope.bib.title\n };\n }\n }\n });\n return false;\n };\n\n $scope.reviewDlgOpen = function () {\n userService.whenAuthenticatedUserDetails().then(function(){\n $uibModal.open({\n backdrop: false,\n templateUrl: '/app/static/partials/review-modal.html',\n controller: 'ReviewDlgCtrl',\n windowClass: \"modal review-add\",\n resolve: {\n bib: function () {\n return {\n id: $scope.bib.id,\n title: $scope.bib.title\n };\n },\n // myreview: function(kohaReviewSvc){ return kohaReviewSvc.mine; }\n // The button to open this dlg shouldn't appear until reviewSvc has resolved.\n }\n });\n })\n\n return false;\n };\n\n $scope.download = function (format) {\n //FIXME: Do refworks 'import to option.'\n if (format == 'refworks') {\n var msgbox = kohaDlg.dialog({\n heading: 'Export to RefWorks',\n message: 'If you are logged in to RefWorks, click \"Direct export\" to send the record directly to your RefWorks database. Choose \"Download\" to import manually.',\n buttons: [{\n label: 'Download',\n val: 'download'\n }, {\n label: 'Direct export',\n val: 'send'\n }]\n });\n\n msgbox.result.then(function (result) {\n if (result === 'send') {\n $http.get('/api/work/' + $scope.bib.id + '/exports/refworks').\n then(function (rsp) {\n var href = 'https://www.refworks.com/express/ExpressImport.asp?vendor=Koha&filter=Marc%20Format&encoding=65001';\n var form = $('
').attr('action', href).attr('target', 'RefWorksMain');\n var textarea = $(''\n }).then(function(data) {\n var newVal = [];\n angular.forEach(data.input.split(/[\\r\\n]/), function(row) {\n if (row)\n newVal.push(row.split('\\t'));\n });\n if (attr.model) {\n var array = parentScope.$eval(attr.model);\n array.replaceWith(newVal);\n }\n else if (attr.apply) {\n var applyFunc = parentScope.$eval(attr.apply);\n applyFunc(newVal);\n }\n });\n };\n }\n };\n}]);\n\nmodule.directive('kwDynamicForm', function() {\n return {\n restrict: 'E',\n scope: {\n parameters: '=kwdfParameters',\n },\n templateUrl: '/app/static/partials/components/kw-dynamic-form.html',\n link: function(scope, elem, attrs) {\n angular.forEach(scope.parameters, function(p) {\n if (p.type == 'matrix') {\n if (!p.value) p.value = [];\n }\n });\n }\n };\n});\n\nmodule.directive('kwDynamicFormConfig', function() {\n return {\n restrict: 'E',\n scope: {\n parameters: '=kwdfcParameters',\n },\n link: function(scope, elem, attr) {\n scope.types = [\n {display: \"Text input\", value: \"text\"},\n {display: \"Select (dropdown)\", value: \"select\"},\n {display: \"Large text (textarea)\", value: \"textarea\"},\n {display: \"Fixed-width matrix\", value: \"matrix\"}\n ];\n scope.newParameter = function() {\n scope.parameters.push({type: 'text', values: [], columnHeader: [], columns: 1 });\n };\n },\n templateUrl: '/app/static/partials/components/kw-dynamic-form-config.html',\n };\n});\n\nmodule.directive('kwMatrixInput', function() {\n return {\n restrict: 'E',\n scope: {\n matrix: '=kwmiModel',\n columnHeader: '=kwmiColumnHeader',\n columns: '=kwmiColumns',\n },\n templateUrl: '/app/static/partials/components/kw-matrix-input.html',\n link: function(scope, elem, attr) {\n scope.columnTemplate = [];\n scope.columnTemplate.length = scope.columns;\n scope.newRow = function() {\n var row = [];\n row.length = scope.columns;\n scope.matrix.push(row);\n };\n\n }\n };\n});\n\nmodule.directive('bvArrayInput', function() {\n return {\n restrict: 'E',\n scope: {\n array: '=model',\n },\n templateUrl: '/app/static/partials/components/bv-array-input.html',\n link: function(scope, elem, attr) {\n scope.newCol = function() {\n scope.array.push('');\n };\n }\n };\n});\n\nmodule\n.directive('kwCheckinMsg', [\"$compile\", function($compile){\n\n var msgs = {\n 'checkin_fail' : { alert: 'danger',\n short: 'Item NOT checked in.'},\n 'not_found' : { alert: 'danger',\n short: 'Barcode not found.'},\n 'not_issued' : { alert: 'danger',\n short: 'Not checked out.'},\n 'withdrawn' : { alert: 'danger',\n long: 'Item is WITHDRAWN. Not checked in.',\n short: 'Item WITHDRAWN.'},\n 'was_lost' : { alert: 'warning',\n long: \"Item was marked lost [{{old_lost}}], now found.\",\n short: 'Item was LOST.'},\n 'hold_requeued' : { alert: 'danger',\n short: 'Trapped hold requeued.'},\n 'unhandled_hold' : { alert: 'warning',\n short: 'Trapped hold ignored.'},\n 'unhandled_lost' : { alert: 'warning',\n short: 'Lost Item record retained on patron account.'},\n 'was_on_order' : { alert: 'warning',\n short: 'Item was On Order.',\n long: \"Item was On Order [{{old_notforloan|displayName:'notforloan'}}], now Available.\"},\n 'unprocessed_callslip' : { alert: 'warning',\n short: 'Requested by callslip.',\n long: 'Item requested by callslip, but not yet processed. '},\n 'cancelled_callslip' : { alert: 'warning',\n short: 'Cancelled callslip.',\n long: 'Item routed by cancelled callslip.'},\n 'item_status' : {\n short: 'Custom Item Status: {{name}}',\n long: 'Item Status : {{name}} ({{date|kohaDate}}) ' +\n '
{{desc}}
' +\n '
Please follow local procedures for this attribute.
'\n },\n 'transfer_received' : {\n short: 'Transfer received.'},\n 'local_use' : {\n short: 'Item logged for local use statistics.'},\n };\n\n return {\n restrict: 'A',\n scope: {\n msg: '=kwCheckinMsg',\n },\n link: function(scope,el,attrs){\n\n if(! msgs[scope.msg.code]) return;\n\n var alertClass = msgs[scope.msg.code].alert || 'info';\n if(el.hasClass('alert')) alertClass = 'alert-' + alertClass;\n el.addClass(alertClass);\n\n for ( var p in scope.msg.param ){\n scope[p] = scope.msg.param[p];\n }\n var tmpl = msgs[scope.msg.code].short;\n if( 'verbose' in attrs && msgs[scope.msg.code].long ){\n tmpl = msgs[scope.msg.code].long;\n }\n tmpl = '' + tmpl + '';\n if(tmpl.match('{{')){\n tmpl = $compile(tmpl)(scope);\n }\n el.append( tmpl );\n\n }};\n}])\n\n\n.factory('bvStrTmplParse', [\"$filter\", function($filter){\n function mkRenderer(expr){\n if(expr.charAt(0)=='%'){\n expr = expr.substring(2,expr.length-1);\n var chain = expr.split('|');\n var key = chain.shift();\n\n return function(data){\n var output = data[key];\n // Empty values are not passed to filters.\n if(output===null || output==='' || output===undefined)\n return '';\n chain.forEach(function(filterSpec){\n var filterArgs = filterSpec.split(':');\n var filterName = filterArgs.shift();\n try {\n var filter = $filter(filterName);\n filterArgs.unshift(output);\n output = filter.apply(this, filterArgs);\n } catch ( e ) {\n console.warn(e);\n return;\n }\n });\n return output;\n };\n } else {\n // const\n return function(){ return expr; };\n }\n }\n return function(tmplStr){\n this.parsedTmpl = tmplStr.split(/(%\\{[^}]+\\})/).filter(function(str){ return str; }).map(\n function(tmplFrag){ return mkRenderer(tmplFrag); }\n );\n this.render = function(obj){\n var d = this.parsedTmpl.reduce(function(str,render){\n return str + render(obj);\n },'');\n return d;\n }\n\n }\n}])\n.directive('bvStrTmpl', [\"bvStrTmplParse\", function(bvStrTmplParse){\n // simple template string substitution, with $filter access.\n // evaluated against `scope` attr (shallow watched).\n // intended for user-defined template strings.\n\n // e.g.
\n // with $scope.tmpl = \"%{name} %{type|wrap:()} -- %{desc|ucfirst}, at %{date|dateFmt:'MM/DD/YYYY'} // %{sz|filesize}\";\n // $scope.foo = { name: 'Example', date: '2019-06-06', desc: 'ok, go.', type: 'thing', sz: 1766339 };\n\n return {\n scope: {\n tmpl: '@bvStrTmpl',\n scope: '='\n },\n link: function(scope, el, attrs){\n\n var parsedTmpl;\n scope.$watch('tmpl', function(tmpl){\n parsedTmpl = new bvStrTmplParse(tmpl);\n if(scope.scope) el.html(parsedTmpl.render(scope.scope));\n });\n scope.$watch('scope', function(data){\n el.html(parsedTmpl.render(data));\n });\n }\n };\n}])\n\n.directive('kwTriggerable', [\"$state\", \"$window\", \"kwHotkeySvc\", function( $state, $window, kwHotkeySvc){\n\n // registers hotkey actions with kmHotkeySvc.\n // deregisters when scope destroyed.\n\n return {\n link: function(scope, el, attrs){\n var key = attrs.kwAssignKey;\n var action;\n if(attrs.kwAction){\n action = function(){ scope.$eval(attrs.kwAction); };\n } else if(attrs.ngClick){\n action = function(){ scope.$eval(attrs.ngClick); };\n } else if(attrs.uiSref){\n var m = attrs.uiSref.split('(');\n var state = m[0];\n var params = (m[1]) ? m[1].substr(0, m[1].length-1) : null;\n var opts = scope.$eval(attrs.uiSrefOpts);\n action = function(){ var eparams=scope.$eval(params);\n if(attrs.target){\n $window.open( $state.href( state, eparams, opts ), attrs.target );\n } else {\n $state.go(state, eparams, opts);\n }};\n } else {\n action = function(){ el[0].click(); };\n }\n var assignedKey = kwHotkeySvc.register(attrs.kwTriggerable, action, key);\n\n if(!key && assignedKey.length==1){\n // note this doesn't handle modifier keys (incl shift), special chars.\n // we should traverse child contents and look at textNodes, but for the moment all\n // uses are just the first text node, so we'll avoid doing this yet.\n var textNode;\n var matchedChar;\n var escapedKey = assignedKey[0].replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&');\n\n var re = new RegExp(escapedKey,'i');\n var childnodes = $(el).contents();\n for (var i = 0; i < childnodes.length; i++) {\n if(childnodes[i].nodeType==3){\n var mtch = childnodes[i].textContent.match( re );\n if(mtch){\n textNode = childnodes[i];\n matchedChar = mtch[0];\n break;\n }\n }\n }\n\n // this only adds class to the character in menu .\n // should perhaps add key to pull-right element if requested ?\n\n if(textNode){\n var splitat = textNode.textContent.search(re);\n // var classname = (attrs.kwSubmenu) ? 'submenu-hotkey' : 'hotkey';\n var classname = 'hotkey-key';\n $(textNode).replaceWith( textNode.textContent.substr(0,splitat) + ''+\n matchedChar + '' + textNode.textContent.substr(splitat+1) );\n }\n\n }\n // FIXME: there's no protection against multiple registrations, but a single deregistration will remove the handler.\n scope.$on('$destroy', function(){\n kwHotkeySvc.deregister(attrs.kwTriggerable);\n });\n }\n };\n}])\n\n.directive('kwOverdrive', [\"$uibModal\", \"kwOverdriveTitleModel\", \"configService\", \"alertService\", \"userService\", function( $uibModal, kwOverdriveTitleModel, configService, alertService, userService){\n return {\n scope: {\n kohaBib: '=',\n // previewOnImg: '@' // look-inside button can find & attach to cover-img el.\n },\n templateUrl: '/app/static/partials/components/overdrive-dl.html',\n\n link: function(scope, el, attrs){\n scope.config = configService;\n scope.user = userService;\n\n if( ! configService.overdrive )\n return ;\n\n var special = ['zeroth','first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth', 'tenth', 'eleventh', 'twelvth', 'thirteenth', 'fourteenth', 'fifteenth', 'sixteenth', 'seventeenth', 'eighteenth', 'nineteenth'];\n var deca = ['twent', 'thirt', 'fourt', 'fift', 'sixt', 'sevent', 'eight', 'ninet'];\n\n scope.stringifyNum = function(n) {\n if (n < 20) return special[n];\n if (n%10 === 0) return deca[Math.floor(n/10)-2] + 'ieth';\n return deca[Math.floor(n/10)-2] + 'y-' + special[n%10];\n };\n\n scope.returnTitle = function(odTitle){\n odTitle.returnTitle().then(function(){\n alertService.add({\n msg: \"Title returned.\",\n type: \"success\"\n });\n });\n };\n scope.removeHold = function(odTitle){\n odTitle.removeHold().then(function(){\n alertService.add({\n msg: \"Your request for this title has been canceled.\",\n type: \"success\"\n });\n });\n };\n scope.overdrivePreview = function(){\n\n if(!scope.odTitle.sampleUri) return;\n $uibModal.open({\n backdrop: 'static',\n size: 'overdrive', //expand to near full width\n window: 'modal',\n templateUrl: '/app/static/partials/overdrivepreview-modal.html',\n controller: [\"$scope\", \"$sce\", \"uri\", \"title\", function($scope,$sce,uri,title){\n $scope.uri = $sce.trustAsResourceUrl(scope.odTitle.sampleUri);\n $scope.title = title;\n }],\n resolve: {\n title: function(){\n return scope.kohaBib.title;\n },\n uri : function(){\n return scope.odTitle.sampleUri;\n }\n }\n });\n };\n scope.checkout = function(){\n scope.odTitle.checkout().then(function(){\n alertService.add({\n msg: \"Title successfully checked out. Select 'Access Title' to download/access.\",\n type: \"success\"\n }), function(fail){\n console.warn(fail);\n var msg = fail.message;\n if(fail.errorCode==\"PatronHasExceededCheckoutLimit\")\n msg = \"Checkout limit reached. You must return other digital titles to access this title.\";\n alertService.add({ msg: msg, type: \"error\" });\n }\n });\n };\n scope.placeHold = function(){\n // set autoCheckout && email prefs.\n\n scope.odTitle.placeHold().then(function(){\n var msg = \"Title successfully requested. When it is available, \";\n if(userService.email)\n msg += \"You'll receive an email at \" + userService.email + ' and ';\n msg += \" you can access the material from this page or your user account page. \";\n\n alertService.add({\n msg: msg,\n type: \"success\"\n });\n });\n };\n\n scope.formatIcon = {\n ebook: \"/app/static/css/overdrive/ebook.png\",\n audio: \"/app/static/css/overdrive/audio.png\",\n video: \"/app/static/css/overdrive/video.png\",\n };\n\n var dereg = scope.$watch('kohaBib', function(bib){\n\n if(bib && bib.marc.subfield(\"037b\") == \"OverDrive, Inc.\"){\n var prodUUID = bib.marc.subfield(\"037a\");\n var re =/ContentDetails.htm\\?ID=\\{(.+)\\}/i;\n var odData = {};\n bib.marc.fields('856').forEach(function(f){\n if( (f.subfield(\"\") == \"Excerpt\" || f.subfield(\"z\") == \"Sample\") && f.subfield(\"u\")){\n odData.sampleUri = f.subfield(\"u\").replace('http:','https:');\n }\n if(!prodUUID){\n var m = f.subfield('u').match(re);\n if(m) prodUUID = m[1];\n }\n });\n\n if(prodUUID){\n scope.odTitle = new kwOverdriveTitleModel(prodUUID, odData);\n\n scope.$on('loggedin', function(){\n scope.odTitle.fetching = true;\n userService.overdrive.patron().finally(function(){\n scope.odTitle.reload();\n });\n });\n el.removeClass('hidden');\n\n if( scope.odTitle.sampleUri){\n if('previewOnImg' in attrs){\n var target = el.closest('.cover-panel');\n if(target.length) {\n var previewBtn = $(''+\n '').click(function(e){\n scope.overdrivePreview();\n });\n previewBtn.prependTo(target);\n scope.$on('$destroy', function(){\n previewBtn.remove();\n })\n } else {\n scope.includeSampleBtn = true;\n }\n\n } else {\n scope.includeSampleBtn = true;\n }\n }\n }\n\n dereg();\n }\n });\n }\n };\n}])\n\n.directive('bvGlobalBreadcrumbs', [\"bvSearchToPick\", \"kwApi\", function(bvSearchToPick, kwApi){\n return {\n scope: true,\n templateUrl: '/app/static/partials/components/global-breadcrumbs.html',\n link: function(scope,el,attrs){\n // We're only doing search-to-pick breadcrumbs for now.\n\n scope.picker = bvSearchToPick;\n scope.$watch('picker.patronid', function(id){\n if(id){\n scope.patron_name = id;\n kwApi.Patron.get({id: id}, function(patron){\n scope.patron_name = patron.surname + ', ' + patron.firstname;\n });\n } else {\n scope.patron_name = null;\n }\n });\n }\n };\n}])\n\n.directive('kwNovelistTitleList', function(){\n return {\n scope: {\n titleList: '=',\n showLessNum: '@',\n showMoreNum: '@'\n },\n templateUrl: '/app/static/partials/novelist-title-list.html',\n\n link: function(scope, el, attrs){\n var showRange = [ attrs.showLessNum||3, attrs.showMoreNum||13];\n scope.showingMore = false;\n scope.numShown = showRange[0];\n\n scope.$watch('showingMore', function(v){\n scope.numShown = (v) ? showRange[1] : showRange[0];\n });\n scope.$watch('titleList', function(list){\n if(list){\n scope.hasMore = list.length > showRange[0];\n list.forEach(function(title){\n title.author_display = title.full_author || title.author;\n title.title_display = title.full_title || title.full_name;\n // TODO; series search link.\n });\n }\n });\n\n },\n controller: [\"$scope\", function($scope){\n $scope.authorSearch = function(author){\n return \"/app/search/author:(\" + encodeURIComponent(author) + \")\";\n };\n }]\n };\n})\n.directive('kwNovelistRelatedContent', function(){\n return {\n scope: {\n novelistContent: '='\n },\n templateUrl: '/app/static/partials/novelist-related-content.html',\n\n link: function(scope, el, attrs){\n scope.$watch('novelistContent', function(content){\n if(content && content.length==1) content[0]._showing = true;\n });\n },\n controller: [\"$scope\", function($scope){\n\n }]\n };\n})\n.directive('kwNovelistRelatedContentLink', [\"$http\", \"SearchQuery\", \"$state\", \"kwLuceneParser\", function( $http, SearchQuery, $state, kwLuceneParser){\n return {\n scope: {\n kwNovelistRelatedContentLink: '='\n },\n template: '{{clink.title}}',\n\n link: function(scope, el, attrs){\n scope.$watch('kwNovelistRelatedContentLink', function(content){\n scope.clink = {\n title: content.title,\n ui: content.ui,\n url: content.links[0].url,\n log_url: content.links[0].log_url\n };\n });\n },\n controller: [\"$scope\", \"$uibModal\", function($scope, $uibModal ){\n $scope.openModal = function(clink){\n // renders html content returned from novelist api in modal.\n var novelistui = clink.ui;\n var url = clink.url.replace('http://','//');\n var httpReq = $http.get(url);\n var modal = $uibModal.open({\n template: '' +\n '
',\n windowClass: 'novelistContent novelist-ui-' + novelistui,\n size: 'lg',\n controller: [\"$scope\", \"$uibModalInstance\", function($scope, $uibModalInstance){\n httpReq.then(function(){\n $scope.modalContent = true;\n });\n }]\n });\n modal.rendered.then(function(){\n var $iframe = $('.novelist-ui-' + novelistui + ' iframe');\n var iframewin = $iframe[0].contentWindow;\n httpReq.then(function(rsp){\n\n // response is json wrapped in parens ( what we supposed to eval it ?!)\n var json = rsp.data.replace(/^\\s*\\(|\\);\\s*$/g, '');\n\n try {\n var htmldata = JSON.parse(json);\n iframewin.document.write(htmldata.body);\n $(iframewin.document).find('a').each(function(el){\n if($(this).attr('data-novelist-custom-link-type')=='AUTILink'){\n var query = kwLuceneParser.composeSubqueries(\n [\n {field: 'author', q: $(this).attr('data-novelist-cl-author')},\n {field: 'title', q: $(this).attr('data-novelist-cl-title')},\n ]\n );\n var searchQuery = new SearchQuery( { q: query });\n $(this).click(function(){\n modal.close();\n $state.go( 'search-results.koha', searchQuery.stateParams() );\n });\n }\n });\n iframewin.document.close();\n } catch (e) {\n console.warn(e);\n modal.close();\n }\n\n }, function(err){\n console.warn(err);\n modal.close();\n });\n });\n };\n }]\n };\n}])\n.directive('kwNovelistGoodreads', function(){\n return {\n scope: {\n novelistContent: '='\n },\n template: '
GoodReads Rating:' +\n '' +\n '({{novelistContent.ratings_count}})' +\n '  ' +\n 'View on goodreads' +\n '
',\n link: function(scope, el, attrs){\n scope.$watch('novelistContent', function(content){\n scope.url = '//goodreads.com/book/show/' + content.work_id;\n });\n }\n };\n})\n.directive('kwNovelistLexile', function(){\n return {\n scope: {\n novelistContent: '='\n },\n template: '
Reading Level:' +\n '
  • Audience: {{novelistContent.AudienceLevel}}
  • ' +\n '
  • Lexile: {{novelistContent.Lexile}} [Grade: {{gradeLevel}}]
  • ' +\n '
',\n link: function(scope, el, attrs){\n var lexileRanges = [[190, 530], [420, 650], [520, 820], [740, 940], [830, 1010], [925, 1070], [970, 1120], [1010, 1185], [1050, 1260], [1080, 1335], [1185, 1385], [1185, 1385]];\n // From Common Core (2012) // i+1 == grade\n scope.$watch('novelistContent', function(content){\n if(content && content.Lexile){\n var score = content.NumericLexile;\n if(score < lexileRanges[0][0]){\n scope.gradeLevel = \"< 1\";\n } else if(score > lexileRanges[10][1]){\n scope.gradeLevel = \"12+\";\n } else {\n var startGrade, endGrade;\n lexileRanges.forEach(function(range, i){\n if( range[0] <= content.NumericLexile && content.NumericLexile <= range[1] ){\n endGrade = i+1;\n if(!startGrade)\n startGrade = i+1;\n }\n scope.gradeLevel = (startGrade == endGrade) ? startGrade : ( startGrade + \"-\" + endGrade);\n });\n }\n\n }\n if(content)\n scope.url = '//goodreads.com/book/show/' + content.work_id;\n });\n }\n };\n})\n\n.directive('logClickEvent', [\"Logger\", function(Logger) { // current use: log novelist clicks\n return {\n restrict: 'A',\n link: function (scope, element, attrs) {\n element.bind('click', function(event) {\n Logger.logEvent(event.toElement.attributes.logurl.value);\n });\n }\n }\n}])\n\n.directive('kwMaybeLink', function( ) {\n return {\n scope: { kwMaybeLink: '@' },\n link: function(scope, el, attrs) {\n scope.$watch('kwMaybeLink', function(uri){\n if(uri){\n var p = el.parent('a.dynamic-link');\n if(p.length){\n p.attr('href', uri);\n } else {\n el.wrap('');\n }\n }\n });\n }\n };\n})\n\n.directive('bvBibShelfBrowse', [\"userService\", \"kohaDlg\", \"$http\", \"configService\", function( userService, kohaDlg, $http, configService ) {\n\n // renders button to open shelf browser from title, with location selection.\n return {\n templateUrl: '/app/static/partials/components/shelfbrowse-launcher.html',\n scope: { bib: '=' },\n link: function(scope, elem, attrs) {\n scope.$watch('bib', function(bib){\n\n if(!bib) return;\n function getItemAt (loc) {\n var param = {\n cn: loc.callnum,\n align: 0,\n window_size: 2\n }\n return $http.get( '/api/branch/'+loc.branch+'/item-shelf/'+loc.location, {params: param} ).then(\n function(rsp){\n if(!rsp.data.length) return null;\n return parseInt(rsp.data[rsp.data.length-1].item_uri.match(/\\d+/));\n }).catch(function(e){\n return null;\n })\n }\n var locations = angular.copy(bib.summary.items_at)\n .filter(function(w) {\n if(w.count > w.suppressed_count) return (w.callnum && w.location);\n });\n scope.location = {\n preferred : [],\n elsewhere : [],\n count : locations.length,\n showList : false\n };\n if(scope.location.count == 1){\n getItemAt(locations[0])\n }\n scope.pickLocation = function($event){\n $event.preventDefault();\n $event.stopPropagation();\n scope.location.showList = !scope.location.showList;\n }\n scope.openShelfBrowser = function(loc){\n if(scope.location.count == 1) loc = locations[0];\n getItemAt(loc).then(function(itemid){\n if(itemid)\n kohaDlg.browseShelf(itemid);\n })\n scope.location.showList = false;\n }\n\n userService.whenAnyUserDetails().then(function(user){\n\n locations.forEach(function(loc){\n loc.branchDisplay = configService.interpolator('branch').display(loc.branch);\n if( user.branchcode == loc.branch ||\n configService.search_group.branches.indexOf( loc.branch ) >= 0 ){\n scope.location.preferred.push(loc);\n } else {\n scope.location.elsewhere.push(loc);\n }\n });\n scope.location.preferred.sort(function(a,b) { return (a.branchDisplay < b.branchDisplay ? -1 : a.branchDisplay > b.branchDisplay ? 1 : 0) });\n scope.location.elsewhere.sort(function(a,b) { return (a.branchDisplay < b.branchDisplay ? -1 : a.branchDisplay > b.branchDisplay ? 1 : 0) });\n // TODO: if > 5 non-preferred, collapse\n });\n })\n\n }\n };\n}])\n\n.directive('kwStaffNav', [\"syncTemplateCache\", \"kohaDlg\", function( syncTemplateCache, kohaDlg) {\n return {\n restrict: 'E',\n template: syncTemplateCache.get('/app/static/partials/staff-nav.html'),\n link: function(scope, elem, attrs) {\n scope.openHotkeyModal = kohaDlg.hotkeyModal;\n }\n };\n}])\n\n.directive('kwCloudLibrary', [\"userService\", \"configService\", \"kwCloudLibraryTitleModel\", function (userService, configService, kwCloudLibraryTitleModel) {\n return {\n scope: {\n cloudBib: '=',\n },\n templateUrl: '/app/static/partials/components/cloudLibary-dl.html',\n link: function (scope, el, attrs) {\n scope.config = configService;\n\n var special = ['zeroth','first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth', 'tenth', 'eleventh', 'twelvth', 'thirteenth', 'fourteenth', 'fifteenth', 'sixteenth', 'seventeenth', 'eighteenth', 'nineteenth'];\n var deca = ['twent', 'thirt', 'fourt', 'fift', 'sixt', 'sevent', 'eight', 'ninet'];\n\n scope.stringifyNum = function(n) {\n if (n < 20) return special[n];\n if (n%10 === 0) return deca[Math.floor(n/10)-2] + 'ieth';\n return deca[Math.floor(n/10)-2] + 'y-' + special[n%10];\n };\n\n scope.loginDlgOpen = function(){\n userService.whenAuthenticatedUser().then(function(){\n\n })\n }\n\n var dereg = scope.$watch('cloudBib', function (bib) {\n if ( bib && bib.marc.subfield(\"037b\") == \"cloudLibrary\" ){\n var prodUUID = bib.marc.subfield(\"037a\");\n\n if (!prodUUID) {\n var re = /document_id-(.*)$/i;\n bib.marc.fields('856').forEach(function(f){\n var m = f.subfield('u').match(re);\n if(m) prodUUID = m[1];\n });\n }\n\n if (prodUUID) {\n scope.cLTitle = new kwCloudLibraryTitleModel(prodUUID);\n el.removeClass('hidden');\n }\n\n dereg();\n }\n });\n }\n };\n}])\n\n\n// TODO refactor into module\n\n.directive('communityContentRating', [\"communityContentSvc\", \"userService\", function (communityContentSvc, userService) {\n return {\n scope: {\n isbn: '=',\n },\n templateUrl: function(elem,attrs) {\n return '/app/static/partials/components/community-content/rating-' + attrs.role + '.html';\n },\n link: function(scope, elem, attrs) {\n scope.svc = communityContentSvc;\n scope.ratingLoaded = false;\n scope.userRatingLoaded = false;\n scope.user = userService;\n\n scope.refreshRating = function() {\n communityContentSvc.getRating(scope.isbn).then(function(rv) {\n scope.ratingLoaded = true;\n scope.rating = rv;\n });\n };\n scope.refreshRating();\n\n communityContentSvc.getUserRating(scope.isbn).then(function(rating) {\n var dereg = scope.$watch('rating.rating', function(newVal, oldVal) {\n if ((oldVal || oldVal === null) && (newVal!==oldVal)) {\n if (!scope.userRating) {\n scope.userRating = newVal;\n }\n dereg();\n }\n });\n\n scope.userRatingLoaded = true;\n scope.userRating = rating;\n scope.$watch('userRating', function(newVal, oldVal) {\n if (!userService.can({catalogue:{bib_ratings:'modify'}})) return;\n if (newVal && (newVal !== oldVal)) {\n communityContentSvc.setUserRating(scope.isbn, newVal).then(function(rv) {\n scope.refreshRating();\n });\n }\n });\n });\n }\n };\n}])\n\n.directive('communityContentComments', [\"communityContentSvc\", \"userService\", function(communityContentSvc, userService) {\n return {\n scope: {\n isbn: '=',\n },\n templateUrl: function(elem,attrs) {\n return '/app/static/partials/components/community-content/comments-' + attrs.role + '.html';\n },\n link: function(scope, elem, attrs) {\n scope.svc = communityContentSvc;\n scope.commentsLoaded = false;\n scope.user = userService;\n\n communityContentSvc.getComments(scope.isbn).then(function(comments) {\n scope.commentsLoaded = true;\n scope.comments = comments;\n });\n scope.showComments = false;\n\n scope.newComment = '';\n\n scope.submit = function(e) {\n if (e.keyCode == 13 && !e.shiftKey) {\n scope.submitComment();\n }\n };\n\n scope.submitComment = function() {\n communityContentSvc.addComment(scope.isbn, scope.newComment).then(function(newComment) {\n scope.comments.unshift(newComment);\n scope.newComment = '';\n }, function(err) {\n // TODO something\n });\n };\n }\n };\n}])\n\n.directive('bvMasthead', [\"$window\", \"syncTemplateCache\", \"$rootScope\", function( $window, syncTemplateCache, $rootScope) {\n return {\n scope: false,\n template: syncTemplateCache.get('/app/static/partials/masthead/' + (KOHA.config.MastheadSkin || 'default') + '.html'),\n controller: [\"$scope\", function($scope) {\n $scope.userjs = function() {\n //console.log(\"*** onready function\");\n $rootScope.$emit('MastheadReady');\n // $window.dispatchEvent(new Event('MastheadReady'));\n $window._mastheadReady = true;\n\n };\n }],\n link: function(scope, el, attrs) {\n\n/* console.log(\"=== bvMasthead link\");\n $window.dispatchEvent(new Event('MastheadReady'));\n $window._mastheadReady = true;*/\n },\n };\n}])\n.directive('isAre', function() {\n return {\n scope: {\n 'isAre' : '='\n },\n link: function(scope, el, attrs) {\n scope.$watch('isAre', function(isAre){\n el.text( (isAre==0 || isAre > 1) ? 'are' : 'is' );\n });\n },\n };\n})\n.directive('bvDateRecur', function() {\n return {\n restrict: 'E',\n templateUrl: '/app/static/partials/components/bv-date-recur.html',\n scope: {\n model: '=', // $resource object.\n withTime: '=',\n },\n link: function(scope, el, attrs){\n scope.weekdays = [\n {id: 1, label: 'Mo'},\n {id: 2, label: 'Tu'},\n {id: 3, label: 'We'},\n {id: 4, label: 'Th'},\n {id: 5, label: 'Fr'},\n {id: 6, label: 'Sa'},\n {id: 7, label: 'Su'},\n ];\n scope.data = {\n pattern: [0,0,0,0,0,0,0],\n lhsLength: 4,\n weekdaySelected: [0,0,0,0,0,0,0],\n };\n scope.patternValid = [true,true,true,true];\n\n scope.isSet = function(s) {\n if (s === undefined || s === null || s === '') return false;\n if (s === 0 || s === '0') return false;\n return true;\n };\n\n scope.$watch('model', function(newVal, oldVal) {\n if (scope.dataUpdated) {\n scope.dataUpdated = false;\n return;\n }\n\n if (newVal) {\n var halves = newVal.split('*');\n var lhs = halves[0].split(':');\n var rhs = halves[1].split(':');\n scope.data.lhsLength = lhs.length;\n for (var i=0; i 1)\n scope.data.lhsLength = scope.data.lhsLength - 1;\n };\n\n scope.shiftRight = function() {\n if (scope.data.lhsLength < 4)\n scope.data.lhsLength = scope.data.lhsLength + 1;\n };\n },\n };\n})\n\n.directive('bvUserContent', [\"$compile\", \"configService\", function( $compile, configService){\n return {\n restrict: 'A',\n template: '
',\n\n link: function(scope, element, attrs) {\n\n// // we bypass ng-bind-html and ngSanitize, since we just trustAsHtml()ed anyway.\n\n var cfg = {\n home: {\n html: configService.layout.maincontent_html,\n widgets: ['bvw-carousel'] // any widgets must have templates with same name.\n }\n };\n var contentBlock = attrs.bvUserContent;\n var tmpl = $('
').html($(cfg[contentBlock].html));\n\n element.html(tmpl);\n\n cfg[contentBlock].widgets.forEach(function(targetEl){\n // all widgets are assumed to be restrict: 'E'.\n var directives = tmpl.find(targetEl).wrap('
');\n // FIXME: This will potentially allow users to initiate compilation of attribute-bound\n // directives ... widgets should all be terminal and high priority.\n directives.each(function(i){\n $compile($(this))(scope.$new());\n });\n });\n\n }\n };\n}])\n\n.directive('bvwCarousel', [\"$http\", \"$q\", \"kwApi\", function($http, $q, kwApi){\n return {\n restrict: 'E',\n templateUrl: '/app/static/partials/components/bvw-carousel.html',\n priority: 1000,\n terminal: true,\n scope: {\n shelfItem: '='\n },\n// Note this directive is exposed to end users.\n\n link: function(scope, el, attrs) {\n // static attributes.\n var crsl = {\n interval: attrs.interval || 0,\n numShown: (attrs.numShown) ? parseInt(attrs.numShown) : 4, // FIXME: Make shrinkable.\n noWrap: attrs.hasOwnProperty('noWrap'),\n maxTitles: attrs.maxTitles || 50,\n searchQuery: attrs.searchQuery,\n searchSort: attrs.searchSort,\n shelfId: attrs.shelfId,\n };\n if(crsl.noWrap){\n el.addClass('no-wrap');\n }\n if(attrs.hasOwnProperty('shelfBrowser')){\n crsl.source = 'shelf-browser';\n } else {\n crsl.source = (crsl.searchQuery) ? 'search' :\n (crsl.shelfId) ? 'shelf' : undefined;\n }\n scope.crsl = crsl;\n crsl.slides = [];\n }, controller : [\"$scope\", function($scope){\n\n $scope.activeSlide = 0;\n $scope.$watch('crsl', function(crsl){\n if(!crsl) return;\n\n // a slide has an array of bib identifiers.\n var middleIndex = ($scope.crsl.numShown % 2) ? Math.floor($scope.crsl.numShown / 2) : undefined;\n var makeSlides = function(biblist, direction){\n // returns an array of arrays.\n var slides = [];\n var slide = [];\n biblist.forEach(function(titlehash, i){\n if(direction==-1) slide.unshift( titlehash );\n else slide.push( titlehash );\n if( i == biblist.length-1 || ! ((i+1) % $scope.crsl.numShown) ){\n var fullSlide = angular.copy(slide);\n // if(middleIndex &&fullSlide.length>middleIndex) fullSlide[middleIndex].classname = 'middle-item';\n if(direction==-1) slides.unshift( { bibs: fullSlide });\n else slides.push( { bibs: fullSlide });\n slide.length = 0;\n }\n });\n return slides;\n };\n\n // bibs:\n if($scope.crsl.source=='search'){\n var solrParams = {\n sort: $scope.crsl.searchSort,\n rows: $scope.crsl.maxTitles || 50,\n facet: false,\n embed: 0,\n spellcheck: false\n };\n // assume search query already encoded.\n $http.get(\"/api/opac/\"+$scope.crsl.searchQuery, { \"params\" : solrParams }).\n then(function(rs){\n $scope.crsl.slides = makeSlides(rs.data.hits.map(function(hit){ return {bibid: hit.id}; }));\n });\n } else if($scope.crsl.source=='shelf'){\n kwApi.WorksList.get({id: $scope.crsl.shelfId}, function(list){\n console.log(list);\n $scope.crsl.slides = makeSlides(list.works.map(function(bib){ return { bibid: bib.id}; }));\n });\n } else if($scope.crsl.source=='shelf-browser' ) {\n // start with dummy slide, just our bib; will fill in remainder.\n // note that the shelf-browser uses {bibid , itemid}, whereas the others just bibid.\n if( ! $scope.crsl.numShown % 2 ) throw('Shelf browser needs odd number of elements.');\n var half_slide_length = parseInt($scope.crsl.numShown / 2);\n var initialItem;\n $scope.$watch( 'shelfItem', function(item){\n if(!item) return;\n\n initialItem = {itemid: item.id, bibid: item.biblionumber, id: item.biblionumber};\n var firstSlide = {\n active: true,\n bibs : [ initialItem ]\n };\n\n var shelf_size = 70;\n var uri_to_id = function(uri){ return parseInt(uri.match(/\\d+/),10); };\n var wing_promises = {};\n [-1,1].forEach(function(align){\n wing_promises[align] = $http.get('/api/item/'+item.id+'/shelf', { params: {align: align, window_size: shelf_size / 2 }})\n .then(function(rsp){\n var shelfitems = rsp.data.map(function(uris){\n return { itemid: uri_to_id(uris.item_uri), bibid: uri_to_id(uris.work_uri) };\n }).reduce(function(acc,cv,i){ // dedupe.\n var last_title = acc[acc.length-1] || initialItem;\n if( last_title.bibid != cv.bibid ){\n acc.push(cv);\n } else {\n last_title.copies = (last_title.copies || 0) + 1;\n // FIXME/TODO: not doing anything with this dupecount yet.\n }\n return acc;\n },[]);\n\n var half_first_shelf;\n if(align==-1){ // leftward\n shelfitems.reverse();\n half_first_shelf = shelfitems.splice(0, half_slide_length );\n for (var i = 0; i < half_first_shelf.length; i++) {\n half_first_shelf[i].id = half_first_shelf[i].bibid;\n firstSlide.bibs.unshift(half_first_shelf[i]);\n }\n return makeSlides(shelfitems,-1);\n } else { // rightward\n half_first_shelf = shelfitems.splice(0, half_slide_length );\n half_first_shelf.forEach(function(itm,i){\n itm.id = itm.bibid;\n firstSlide.bibs.push(itm);\n });\n return makeSlides(shelfitems);\n }\n });\n });\n $q.all([wing_promises[\"-1\"], wing_promises[\"1\"]]).then(function(wings){\n $scope.crsl.slides = wings[0].concat([firstSlide], wings[1]);\n $scope.activeSlide = wings[0].length;\n });\n });\n\n // we could watch for edges and load more, but we'll just have a large shelf.\n }\n });\n\n }]\n };\n}])\n.directive('bvwLazyCoverimgSlide', [\"$timeout\", function($timeout){\n // lazy loads a slide by waiting until previous slide is active,\n // then setting the bibid.\n // assumes a slide has array of objects 'bibs' with a bibid prop, and we set the id prop.\n // this is done since all these slides are added to the dom by uib-carousel, which would\n // otherwise cause all cover images to load but remain hidden.\n return {\n restrict: 'A',\n require: ['^uibCarousel','^bvwCarousel'],\n scope: {\n slideContents: '=bvwLazyCoverimgSlide',\n myIndex: '@'\n },\n link: function(scope, el, attrs, ctrls ) {\n var carouselCtrl = ctrls[0],\n bvwCarouselCtrl = ctrls[1];\n var parent = el.closest('.carousel');\n var myindex = attrs.myIndex; // static.\n\n var triggerImg = function(){\n scope.slideContents.bibs.forEach(function(title){\n if(!title.id) title.id = title.bibid;\n });\n };\n\n var isEdge = (myindex===0 || myindex==carouselCtrl.slides.length);\n\n var dereg = scope.$watch( carouselCtrl.getCurrentIndex , function(activeIndex){\n // ui-bootstrap's carousel doesn't hide controls when no-wrap.\n if(isEdge){\n if(activeIndex===0) parent.addClass('first-slide');\n else parent.removeClass('first-slide');\n if(activeIndex==carouselCtrl.slides.length-1) parent.addClass('last-slide');\n else parent.removeClass('last-slide');\n }\n if( Math.abs(activeIndex - myindex) <= 1 ) {\n // for this, prev, and next slides,\n // set id, which triggers cover-img directive to load the bib\n if(activeIndex==myindex){\n triggerImg();\n } else {\n $timeout(function(){\n triggerImg();\n });\n }\n }\n\n if(!isEdge) dereg();\n });\n }\n };\n}])\n.directive('backendSortable', function(){\n return {\n link: function(scope, el, attrs) {\n var asc = true;\n var sortspec = scope.$eval(attrs.backendSortable);\n if (sortspec.field == attrs.sortField) {\n var iconName = (sortspec.reverse)? 'icon-sort-up':'icon-sort-down';\n el.append('').addClass('sortable sorted sorted-down');\n } else {\n el.append('').addClass('sortable');\n }\n\n el.click(function() {\n if(attrs.sortField == sortspec.field) {\n sortspec.reverse = !sortspec.reverse;\n }\n sortspec.field = attrs.sortField;\n scope.$eval(attrs.sortChanged);\n\n var whichway = (sortspec.reverse) ? 'icon-sort-up' : 'icon-sort-down';\n var otherway = (sortspec.reverse) ? 'icon-sort-down' : 'icon-sort-up';\n var sortedDir = (sortspec.reverse) ? 'sorted-up' : 'sorted-down';\n\n el.siblings('[backend-sortable]').removeClass('sorted sorted-down sorted-up').children('i').removeClass('icon-sort-up icon-sort-down').addClass('icon-sort');\n el.removeClass('sorted-up sorted-down').addClass('sorted '+ sortedDir ).children('i').removeClass('icon-sort ' + otherway).addClass( whichway );\n });\n }\n };\n})\n\n.directive('bvPluralizeCount', function(){\n // like ng-pluralize, but dumber and less verbose to use.\n // thing\n // if pluralization is more complicated than -s -es -or -ies, use ng-pluralize.\n var numStr = ['zero','one','two','three','four','five','six','seven','eight','nine','ten'];\n\n function naivePluralize (str){\n var esRe = /(s|x|ch|sh|zz)$/;\n return (str.charAt(str.length-1)=='y') ?\n str.replace(/y$/,'ies') : (esRe.test(str)) ?\n str.replace(esRe, '$1es') : str + 's';\n }\n\n return {\n restrict: 'A',\n scope: {\n cnt: '=bvPluralizeCount',\n // asText: '@', // commented means it's implemented as static attribute.\n // ucfirst: '@' // mostly for as-text.\n },\n link: function(scope,el,attrs){\n var origText = el.text().trim();\n el.text(' ');\n var pluralText = naivePluralize(origText);\n var cntEl = $('').prependTo(el);\n var textEl = $('').appendTo(el);\n var asText = 'asText' in attrs;\n var ucfirst = 'ucfirst' in attrs;\n scope.$watch('cnt', function(cnt){\n if(cnt===undefined) return;\n var num = cnt;\n if(asText && numStr[cnt]){\n num = numStr[cnt];\n if(ucfirst) num = num.charAt(0).toUpperCase() + num.slice(1);\n }\n textEl.text( (cnt==1) ? origText : pluralText);\n cntEl.text( num );\n });\n }\n };\n})\n\n.directive('bvBibSerialsSummary', [\"kwApi\", \"$q\", \"configService\", \"$filter\", function(kwApi, $q, configService, $filter) {\n\n return {\n templateUrl: '/app/static/partials/bib-serials-summary.html',\n scope: {\n iface: '@',\n periodicals: '=', // i.e. kwApi.Periodical.getForBib({bibid: bib.id})\n // expandable: '@'\n },\n link: function(scope,el,attrs) {\n\n scope.fixedView = !( 'expandable' in attrs );\n scope.view = {\n expanded: scope.fixedView,\n };\n var statusOrder = [ 'arrived', 'expected', 'late', 'missing', 'unavailable', 'deleted', 'claimed' ];\n var branchOrder = configService.interpolator('bv-branch').listCodes();\n // what about sortOwnBranchFirst ?\n //\n scope.staffNote = {};\n scope.publicNote = {};\n\n scope.$watch('periodicals', function(periodicalsRsrc){\n if(!periodicalsRsrc) return;\n var serialEdCnt = (scope.iface=='staff') ? configService.StaffSerialIssueDisplayCount :\n configService.OPACSerialIssueDisplayCount;\n\n if(serialEdCnt){\n var serialEditions = [];\n var fetchedEditions = [];\n scope.totalSubs = 0;\n var branchSubs = {};\n periodicalsRsrc.$promise.then(function(periodicals){\n periodicals.forEach(function(periodical, pnum){\n fetchedEditions.push( kwApi.Periodical.getSerialEditions({id: periodical.id},\n function(editions){\n // serialEditions are sorted pubdate ASC\n serialEditions = serialEditions.concat( editions.slice( -1 * serialEdCnt));\n }).$promise );\n // in case of multiple periodicals, we just show pattern info from first.\n if(pnum===0){\n var pattern = periodical.receipt_patterns[periodical.pattern_index || 0].pattern;\n var desc = $filter('bvDateRecur')(pattern);\n if(desc != \"[Invalid Pattern]\") scope.pubPatternDesc = desc;\n }\n scope.totalSubs += periodical.total_subscriptions;\n periodical.subscriptions.forEach(function(subSummary, i){\n kwApi.Subscription.get({ id: subSummary.id, view: 'holdings' }, function(sub){\n if (sub.staff_note)\n scope.staffNote[sub.branch_code] = sub.staff_note;\n if (sub.opac_note)\n scope.publicNote[sub.branch_code] = sub.opac_note;\n if(branchSubs[sub.branch_code])\n branchSubs[sub.branch_code].push(sub);\n else\n branchSubs[sub.branch_code] = [ sub ];\n if(i+1==periodical.subscriptions.length){\n scope.branchSubs = [];\n configService.interpolator('bv-branch').listCodes().forEach(function(branchcode){\n if(branchSubs[branchcode])\n scope.branchSubs.push({branch: branchcode, sub: branchSubs[branchcode]});\n });\n }\n });\n });\n });\n $q.all(fetchedEditions).then(function(){\n\n serialEditions.sort(function(a,b){\n return (a.publication_date > b.publication_date ? -1 :\n a.publication_date < b.publication_date ? 1 : 0);\n });\n scope.serialEditions = serialEditions.slice(0, serialEdCnt);\n scope.serialEditions.forEach(function(issue){\n // status.branch is obj, status.branches is array.\n issue.statuses = [];\n statusOrder.forEach(function(statusCode){\n if((issue.count[statusCode]||{}).total){\n var stats = issue.count[statusCode];\n stats.status = statusCode;\n // serialedition.statuses[i].branches[j].count\n stats.branches = [];\n branchOrder.forEach(function(branchcode){\n if(stats.branch[branchcode]){\n stats.branches.push({ code: branchcode, count: stats.branch[branchcode] });\n }\n });\n issue.statuses.push(issue.count[statusCode]);\n }\n });\n });\n });\n });\n }\n });\n }\n };\n}])\n\n.directive('bvTextualHoldings', function() {\n return {\n template: '{{label}} ' +\n '[{{expand ? \"hide\" : \"show\"}}]' +\n '
' +\n '1 || h.tag!=\\'866\\'\">({{h.prefix}}) {{h.value}}
',\n scope: {\n label: '@',\n marc: '=',\n staff: '@',\n fixed: '@',\n },\n link: function(scope, elem, attrs) {\n var fields = [\n {tag: '866', prefix: 'a'},\n {tag: '867', prefix: 'c'},\n {tag: '868', prefix: 'd'},\n ];\n\n if (scope.fixed) {\n scope.expand = true;\n }\n else {\n scope.expand = (scope.staff && (scope.staff !== 'false') ? true : false);\n }\n\n scope.holdings = [];\n scope.$watch('marc', function(newVal) {\n if (newVal) {\n fields.forEach(function(f) {\n scope.marc.fields(f.tag).forEach(function(marcField){\n var subs = [];\n if (scope.staff) {\n if (marcField.subfield('x'))\n subs.push(marcField.subfield('x'));\n }\n else {\n if (marcField.subfield('z'))\n subs.push(marcField.subfield('z'));\n }\n if (marcField.subfield('a'))\n subs.push(marcField.subfield('a'));\n if (subs.length) {\n scope.holdings.push({\n tag: f.tag,\n prefix: f.prefix,\n value: subs.join(': '),\n });\n }\n });\n });\n }\n });\n }\n };\n})\n\n// Infrastructural stuff\n\n\n// Directives to invoke a function call when a template and its children have loaded\n//\n// 1. Add the following to your template html AT THE BOTTOM\n//\n// \n//\n// where \"myfunc\" is a function in your template's scope, \"something\" is an arbitrary name\n// shared with bvao-async-onready-await directives, and \"2\" means that the function should\n// fire after children of depth 2 have loaded. Note *should*. This works by enqueueing \n// cache-busted template loads. It should work either with depth- or breadth-first order\n// but will NOT work if the backend returns template files severely out of order (see step 2)\n//\n// 2. (Optional) Preload your templates\n//\n// Add to any template you want preloaded, or '.preload' in any directory\n// where you want all templates preloaded. Rerun wack-concat. Then replace all templateUrl\n// statements with\n//\n// template: syncTemplateCache.get('/app/static/partials/path/to/template.html');\n//\n// obviously you'll need to inject syncTemplateCache. Note, this has the side benefit of\n// speeding up loading. You can't use ng-include, sorry, you'd need a specific directive\n// that can handle a static synchronous template. This DOES NOT interact with $templateCache\n// in any way\n//\n// 3. Use bv-async-onready-await to delay the function call until specific things are done\n//\n// Let's say you have a directive whose link function does DOM manipulation (questionable\n// but okay). You can add bv-async-onready-await to the attributes\n//\n// \n//\n// then in the link (or post) function for myDirective\n// \n// module.directive('myDirective'), function(bvAsyncOnreadySvc) {\n// return {\n// link: function(scope, elem, attrs) {\n// somePromise.then(function() {\n// bvAsyncOnreadySvc.childReady(attrs.bvaoName, attrs.bvAsyncOnreadyAwait);\n// });\n// }\n// }\n// });\n// \n// The 'pre' function of the bvAsyncOnreadyAwait directive tells the service to wait\n// for the child signal\n\n.directive('bvAsyncOnready', [\"bvAsyncOnreadySvc\", function(bvAsyncOnreadySvc) {\n var cbCount = 0;\n return {\n templateUrl: function(tElem, tAttrs) {\n //console.log(tElem[0].outerHTML);\n //console.log(\"== bvAsyncOnready \" + tAttrs.depth + \" compile\");\n cbCount++;\n return '/app/static/partials/components/bv-async-onready/' + tAttrs.depth + '.html?' + cbCount;\n },\n scope: {\n name: '@bvaoName',\n onReady: '=bvaoOnready',\n },\n compile: function(elem, attrs, transclude) {\n bvAsyncOnreadySvc.register(attrs.bvaoName);\n //console.log(\"== bvAsyncOnready \" + attrs.depth + \" compile\");\n return {\n pre: function(scope, elem, attrs) {\n //console.log(\"== bvAsyncOnready \" + attrs.depth + \" pre with name \" + scope.name);\n },\n post: function(scope, elem, attrs) {\n if (scope.onReady) {\n bvAsyncOnreadySvc.templateReady(scope.name, scope.onReady);\n }\n //console.log(\"== bvAsyncOnready \" + attrs.depth + \" post with name \" + scope.name);\n }\n };\n },\n };\n}])\n\n.directive('bvAsyncOnreadyAwait', [\"bvAsyncOnreadySvc\", function(bvAsyncOnreadySvc) {\n return {\n template: false,\n scope: false,\n restrict: 'A',\n compile: function(tEl, tAt, transclude) {\n return {\n pre: function(scope, elem, attrs) {\n var name = attrs.bvaoName;\n var child = attrs.bvAsyncOnreadyAwait;\n bvAsyncOnreadySvc.child(name, child);\n //console.log(\"== bvAsyncOnreadyAwait registering \" + child + \" in \" + name);\n },\n post: function(scope, elem, attrs) {\n // TODO - support timeout if the normal signal sender never fires\n //console.log(\"== bvAsyncOnreadyAwait post\");\n }\n };\n },\n };\n}])\n\n// Directive pair to intentionally break isolate- or true-scoped directives across generations\n//
\n// \n// \n// \n//\n// and now target-directive gets all of the div's scope elements as if inherited.\n\n\n.directive('bvRegisterScope', [\"$cacheFactory\", function($cacheFactory) {\n return {\n restrict: 'A',\n scope: false,\n link: function(scope, elem, attrs) {\n var cache = $cacheFactory.get('scopeRegistry') || $cacheFactory('scopeRegistry');\n cache.put(attrs.bvRegisterScope, scope);\n },\n };\n}])\n\n.directive('bvInheritScope', [\"$cacheFactory\", function($cacheFactory) {\n return {\n restrict: 'A',\n scope: false,\n link: function(applyTo, elem, attrs) {\n var cache = $cacheFactory.get('scopeRegistry') || $cacheFactory('scopeRegistry');\n var applyFrom = cache.get(attrs.bvInheritScope);\n if (applyFrom) {\n angular.forEach(applyFrom, function(val, key) {\n if (key.substring(0,1) !== \"$\" && applyFrom.hasOwnProperty(key) && !applyTo.hasOwnProperty(key)) {\n applyTo[key] = val;\n }\n });\n }\n },\n };\n}])\n\n.directive('bvSameValueAs', function(){\n return { // repeat password.\n restrict: 'A',\n require: '?ngModel',\n link: function(scope, elm, attr, ctrl) {\n if (!ctrl) return;\n\n var otherValue = 0;\n attr.$observe('bvSameValueAs', function(value) {\n otherValue = value;\n ctrl.$validate();\n });\n ctrl.$validators.nomatch = function(modelValue, viewValue) {\n return ctrl.$isEmpty(viewValue) || viewValue == otherValue;\n };\n }\n };\n\n})\n\n\n.directive('bvMvrForeach', function() {\n return {\n templateUrl: '/app/static/partials/staff/tools/mvr/inc/foreach.html',\n restrict: 'E',\n scope: {\n i: '=model',\n context: '@',\n },\n link: function(scope, elem, attr) {\n if (scope.i && typeof(scope.i) == 'object') {\n scope.o = angular.copy(scope.i);\n scope.valid = scope.i._valid;\n }\n else {\n scope.i = {};\n scope.o = {\n marcs: [{v:''}],\n assertions: [{}],\n };\n }\n\n if (!scope.o.assertions)\n scope.o.assertions = [{}];\n if (!scope.o.marcs)\n scope.o.marcs = [{v:''}];\n \n scope.addMarc = function() {\n scope.o.marcs.push({v:''});\n };\n scope.deleteMarc = function(n) {\n scope.o.marcs.splice(n,1);\n };\n\n scope.addAssertion = function() {\n scope.o.assertions.push({});\n };\n scope.deleteAssertion = function(n) {\n scope.o.assertions.splice(n,1);\n };\n\n var checkValidity;\n checkValidity = function() {\n //console.log(\"Foreach \" + scope.$id + \" digest\");\n\n scope.valid = true;\n scope.o.marcs.forEach(function(m) {\n if (!(m && m.v)) scope.valid = false;\n });\n\n var stripped = [];\n var dsl = [];\n for (var i=0; i 2)\n scope.o.parameters.length = 2;\n }\n });\n\n var checkValidity;\n checkValidity = function() {\n //console.log(\"Boolean \" + scope.$id + \" digest\");\n var dsl;\n if (scope.o.op == 'and' || scope.o.op == 'or') {\n if (scope.o.parameters.length == 1) {\n dsl = scope.o.parameters[0]._dsl;\n scope.valid = scope.o.parameters[0]._valid;\n }\n else {\n dsl = '(' + scope.o.parameters.map(function(e) { return e._dsl }).join(' ' + scope.o.op + ' ') + ')';\n scope.valid = true;\n scope.o.parameters.forEach(function(t) { if (!t._valid) scope.valid = false });\n }\n }\n else if (scope.o.op == 'nor') {\n if (scope.o.parameters.length == 1) {\n dsl = '(not ' + scope.o.parameters[0]._dsl + ')';\n scope.valid = scope.o.parameters[0]._valid;\n }\n else {\n dsl = '(not (' + scope.o.parameters.map(function(e) { return e._dsl }).join(' or ') + '))';\n scope.valid = true;\n scope.o.parameters.forEach(function(t) { if (!t._valid) scope.valid = false });\n }\n }\n else if (scope.o.op == 'if') {\n if (scope.o.parameters.length != 2) {\n scope.valid = false;\n }\n else {\n dsl = '(' + scope.o.parameters[0]._dsl + ' implies ' + scope.o.parameters[1]._dsl + ')';\n scope.valid = scope.o.parameters[0]._valid && scope.o.parameters[1]._valid;\n }\n }\n else if (scope.o.op == 'iff') {\n if (scope.o.parameters.length != 2) {\n scope.valid = false;\n }\n else {\n dsl = '(' + scope.o.parameters[0]._dsl + ' <=> ' + scope.o.parameters[1]._dsl + ')';\n scope.valid = scope.o.parameters[0]._valid && scope.o.parameters[1]._valid;\n }\n }\n scope.i._valid = scope.valid;\n if (scope.valid) {\n scope.i._dsl = dsl;\n scope.i.op = scope.o.op;\n var stripped = angular.copy(scope.o.parameters);\n stripped.forEach(function(p) {\n //delete p._dsl;\n //delete p._valid;\n });\n scope.i.parameters = stripped;\n }\n\n };\n //scope.$on('mvr-register', function() {\n checkValidity();\n\n var dereg = scope.$watch('o', function(newVal, oldVal) {\n if (!(newVal && !angular.equals(newVal, oldVal))) return;\n\n checkValidity();\n }, true);\n scope.$on('$destroy', dereg);\n //});\n\n }\n };\n})\n\n\n.directive('bvMvrBaseContextOption', function() {\n return {\n templateUrl: '/app/static/partials/staff/tools/mvr/inc/base-context-option.html',\n restrict: 'E',\n scope: {\n i: '=model',\n withThis: '@',\n },\n link: function(scope, elem, attr) {\n if (scope.i && typeof(scope.i) == 'object') {\n scope.o = angular.copy(scope.i);\n scope.valid = scope.i._valid;\n }\n else {\n scope.i = {};\n scope.o = {\n op: (scope.withThis ? 'no(@this)|F' : 'no(X)|F'),\n xType: (scope.withThis ? '' : 'fields'),\n xValue: '',\n yType: '',\n yValue: '',\n };\n }\n if (!scope.o.op) {\n scope.o.op = (scope.withThis ? 'no(@this)|F' : 'no(X)|F');\n scope.o.xType = (scope.withThis ? '' : 'fields');\n scope.o.yType = '';\n }\n\n scope.$watch('o.op', function(newVal) {\n if (newVal == 'no(X)|F' || newVal == 'maybe_one(X)|F' || newVal == 'one(X)|F' || newVal == 'any(X)|F') {\n scope.o.xType = 'fields';\n scope.o.yType = '';\n }\n else if (newVal == 'no(@this)|F' || newVal == 'maybe_one(@this)|F' || newVal == 'one(@this)|F' || newVal == 'any(@this)|F') {\n scope.o.xType = '';\n scope.o.yType = '';\n }\n else if (newVal == 'no(X)|S' || newVal == 'maybe_one(X)|S' || newVal == 'one(X)|S' || newVal == 'any(X)|S') {\n scope.o.xType = 'subfields';\n scope.o.yType = '';\n }\n else if (newVal == 'count(X) == Y|F') {\n scope.o.xType = 'fields';\n scope.o.yType = 'number';\n }\n else if (newVal == 'count(@this) == Y|F') {\n scope.o.xType = '';\n scope.o.yType = 'number';\n }\n else if (newVal == 'count(X) == Y|S') {\n scope.o.xType = 'subfields';\n scope.o.yType = 'number';\n }\n\n else if (newVal == 'no(X,Y)|F' || newVal == 'maybe_one(X,Y)|F' || newVal == 'one(X,Y)|F' || newVal == 'any(X,Y)|F' || newVal == 'all(X,Y)|F') {\n scope.o.xType = 'fields';\n scope.o.yType = 'field-subexpr';\n }\n else if (newVal == 'no(@this,Y)|F' || newVal == 'maybe_one(@this,Y)|F' || newVal == 'one(@this,Y)|F' || newVal == 'any(@this,Y)|F' || newVal == 'all(@this,Y)|F') {\n scope.o.xType = '';\n scope.o.yType = 'field-subexpr';\n }\n else if (newVal == 'no(X,Y)|S' || newVal == 'maybe_one(X,Y)|S' || newVal == 'one(X,Y)|S' || newVal == 'any(X,Y)|S' || newVal == 'all(X,Y)|S') {\n scope.o.xType = 'subfields';\n scope.o.yType = 'subfield-subexpr';\n }\n\n else if (newVal == 'X') {\n scope.o.xType = 'boolean';\n scope.o.yType = '';\n }\n else {\n console.log(\"?? \" + newVal);\n }\n });\n\n var checkValidity;\n checkValidity = function() {\n var x = '', y = '';\n scope.valid = true;\n var xStripped = scope.o.xValue;\n var yStripped = scope.o.yValue;\n\n if (scope.o.xType == 'fields') {\n if (typeof(scope.o.xValue) !== 'string') scope.o.xValue = '';\n if (scope.o.xValue && scope.o.xValue !== '')\n x = scope.o.xValue;\n else\n scope.valid = false;\n }\n else if (scope.o.xType == 'subfields') {\n if (typeof(scope.o.xValue) !== 'string') scope.o.xValue = '';\n if (scope.o.xValue && scope.o.xValue !== '')\n x = scope.o.xValue;\n else\n scope.valid = false;\n }\n else if (scope.o.xType == 'boolean') {\n if (typeof(scope.o.xValue) !== 'object') scope.o.xValue = {};\n if (scope.o.xValue._valid) {\n x = scope.o.xValue._dsl;\n xStripped = angular.copy(scope.o.xValue);\n //delete xStripped._dsl;\n //delete xStripped._valid;\n }\n else\n scope.valid = false;\n }\n\n if (scope.o.yType == 'number') {\n if (typeof(scope.o.yValue) !== 'string') scope.o.yValue = '';\n if (/^\\-?[0-9]+(\\.[0-9]+)?$/.test(scope.o.yValue))\n y = scope.o.yValue;\n else \n scope.valid = false;\n }\n else if (scope.o.yType == 'field-subexpr' || scope.o.yType == 'subfield-subexpr') {\n if (typeof(scope.o.yValue) !== 'object') scope.o.yValue = {};\n if (scope.o.yValue._valid) {\n y = scope.o.yValue._dsl;\n yStripped = angular.copy(scope.o.yValue);\n //delete yStripped._dsl;\n //delete yStripped._valid;\n }\n else\n scope.valid = false;\n }\n\n scope.i._valid = scope.valid;\n if (scope.valid) {\n scope.i._dsl = scope.o.op.replace(/\\|.*/,'').replace('X',x).replace('Y',y);\n if (!( /^\\(.*\\)$/.test(scope.i._dsl)))\n scope.i._dsl = '(' + scope.i._dsl + ')';\n scope.i.op = scope.o.op;\n scope.i.xValue = xStripped;\n scope.i.yValue = yStripped;\n scope.i.xType = scope.o.xType;\n scope.i.yType = scope.o.yType;\n }\n };\n //scope.$on('mvr-register', function() {\n checkValidity();\n\n var dereg = scope.$watch('o', function(newVal, oldVal) {\n if (!(newVal && !angular.equals(newVal, oldVal))) return;\n\n checkValidity();\n }, true);\n scope.$on('$destroy', dereg);\n //});\n\n }\n };\n})\n\n.directive('bvMvrFieldContextOption', function() {\n return {\n templateUrl: '/app/static/partials/staff/tools/mvr/inc/field-context-option.html',\n restrict: 'E',\n scope: {\n i: '=model',\n withThis: '@',\n },\n link: function(scope, elem, attr) {\n if (scope.i && typeof(scope.i) == 'object') {\n scope.o = angular.copy(scope.i);\n scope.valid = scope.i._valid;\n }\n else {\n scope.i = {};\n scope.o = {\n op: (scope.withThis ? 'no(@this)' : 'no(X)'),\n xType: (scope.withThis ? '' : 'subfields'),\n xValue: '',\n yType: '',\n yValue: '',\n };\n }\n if (!scope.o.op) {\n scope.o.op = (scope.withThis ? 'no(@this)' : 'no(X)');\n scope.o.xType = (scope.withThis ? '' : 'subfields');\n scope.o.yType = '';\n }\n\n scope.$watch('o.op', function(newVal) {\n if (newVal == 'no(X)' || newVal == 'maybe_one(X)' || newVal == 'one(X)' || newVal == 'any(X)') {\n scope.o.xType = 'subfields';\n scope.o.yType = '';\n }\n else if (newVal == 'no(@this)' || newVal == 'maybe_one(@this)' || newVal == 'one(@this)' || newVal == 'any(@this)') {\n scope.o.xType = '';\n scope.o.yType = '';\n }\n else if (newVal == 'count(X) == Y') {\n scope.o.xType = 'subfields';\n scope.o.yType = 'number';\n }\n else if (newVal == 'count(@this) == Y') {\n scope.o.xType = '';\n scope.o.yType = 'number';\n }\n\n else if (newVal == 'no(X,Y)' || newVal == 'maybe_one(X,Y)' || newVal == 'one(X,Y)' || newVal == 'any(X,Y)' || newVal == 'all(X,Y)') {\n scope.o.xType = 'subfields';\n scope.o.yType = 'subexpr';\n }\n else if (newVal == 'no(@this,Y)' || newVal == 'maybe_one(@this,Y)' || newVal == 'one(@this,Y)' || newVal == 'any(@this,Y)' || newVal == 'all(@this,Y)') {\n scope.o.xType = '';\n scope.o.yType = 'subexpr';\n }\n\n else if (newVal == 'indicator1 == X' || newVal == 'indicator1 != X' || newVal == 'indicator2 == X' || newVal == 'indicator2 != X') {\n scope.o.xType = 'indicator';\n scope.o.yType = '';\n }\n else if (newVal == 'indicators == X' || newVal == 'indicators != X') {\n scope.o.xType = 'indicators';\n scope.o.yType = '';\n }\n else if (newVal == 'X') {\n scope.o.xType = 'boolean';\n scope.o.yType = '';\n }\n else {\n console.log(\"?? \" + newVal);\n }\n });\n\n var checkValidity;\n\n checkValidity = function() {\n //console.log(\"Field context \" + scope.$id + \" digest\");\n var x = '', y = '';\n scope.valid = true;\n var xStripped = scope.o.xValue;\n var yStripped = scope.o.yValue;\n if (scope.o.xType == 'subfields') {\n if (typeof(scope.o.xValue) !== 'string') scope.o.xValue = '';\n if (scope.o.xValue && scope.o.xValue !== '')\n x = scope.o.xValue;\n else\n scope.valid = false;\n }\n else if (scope.o.xType == 'indicator') {\n if ((typeof(scope.o.xValue) !== 'string') || (scope.o.xValue.length != 1)) scope.o.xValue = '';\n if (/^[0-9.#]$/.test(scope.o.xValue))\n x = scope.o.xValue;\n else\n scope.valid = false;\n }\n else if (scope.o.xType == 'indicators') {\n if ((typeof(scope.o.xValue) !== 'string') || (scope.o.xValue.length != 2)) scope.o.xValue = '';\n if (/^[0-9.#][0-9.#]$/.test(scope.o.xValue))\n x = scope.o.xValue;\n else\n scope.valid = false;\n }\n else if (scope.o.xType == 'boolean') {\n if (typeof(scope.o.xValue) !== 'object') scope.o.xValue = {};\n if (scope.o.xValue._valid) {\n x = scope.o.xValue._dsl;\n xStripped = angular.copy(scope.o.xValue);\n //delete xStripped._dsl;\n //delete xStripped._valid;\n }\n else\n scope.valid = false;\n }\n\n if (scope.o.yType == 'number') {\n if (typeof(scope.o.yValue) !== 'string') scope.o.yValue = '';\n if (/^\\-?[0-9]+(\\.[0-9]+)?$/.test(scope.o.yValue))\n y = scope.o.yValue;\n else\n scope.valid = false;\n }\n else if (scope.o.yType == 'subexpr') {\n if (typeof(scope.o.yValue) !== 'object') scope.o.yValue = {};\n if (scope.o.yValue._valid) {\n y = scope.o.yValue._dsl;\n yStripped = angular.copy(scope.o.yValue);\n //delete yStripped._dsl;\n //delete yStripped._valid;\n }\n else\n scope.valid = false;\n }\n\n scope.i._valid = scope.valid;\n if (scope.valid) {\n scope.i._dsl = scope.o.op.replace('X',x).replace('Y',y);\n if (!( /^\\(.*\\)$/.test(scope.i._dsl)))\n scope.i._dsl = '(' + scope.i._dsl + ')';\n scope.i.op = scope.o.op;\n scope.i.xValue = xStripped;\n scope.i.yValue = yStripped;\n scope.i.xType = scope.o.xType;\n scope.i.yType = scope.o.yType;\n }\n \n };\n //scope.$on('mvr-register', function() {\n //console.log(\"Field register\");\n checkValidity();\n\n var dereg = scope.$watch('o', function(newVal, oldVal) {\n if (!(newVal && !angular.equals(newVal, oldVal))) return;\n\n checkValidity();\n }, true);\n scope.$on('$destroy', dereg);\n //});\n }\n };\n})\n\n.directive('bvMvrSubfieldContextOption', function() {\n return {\n templateUrl: '/app/static/partials/staff/tools/mvr/inc/subfield-context-option.html',\n restrict: 'E',\n scope: {\n i: '=model',\n },\n link: function(scope, elem, attr) {\n if (scope.i && typeof(scope.i) == 'object') {\n scope.o = angular.copy(scope.i);\n scope.valid = scope.i._valid;\n }\n else {\n scope.i = {};\n scope.o = {\n op: 'this == X',\n xType: 'string',\n xValue: '',\n };\n }\n if (!scope.o.op) {\n scope.o.op = 'this == X';\n scope.o.xType = 'string';\n }\n \n scope.xTypeOptions = [];\n\n scope.$watch('o.op', function(newVal) {\n if (newVal == 'this == X' || newVal == 'this != X') {\n scope.xTypeOptions = [\n {v:'string',d:'string'},\n {v:'subfield',d:'subfield in this field'},\n {v:'field',d:'another field/subfield'},\n ];\n }\n else if (newVal == 'this =~ X' || newVal == 'this !~ X') {\n scope.xTypeOptions = [\n {v:'regex',d:'regular expression'},\n ];\n }\n else if (newVal == 'length(this) == X' || newVal == 'length(this) < X' || newVal == 'length(this) > X') {\n scope.xTypeOptions = [\n {v:'number',d:'number'},\n ];\n }\n else if (newVal == 'X') {\n scope.xTypeOptions = [];\n scope.o.xType = 'boolean';\n }\n else {\n scope.xTypeOptions = [];\n }\n });\n\n\n scope.checkValidity = function() {\n //console.log(\"Subfield context \" + scope.$id + \" digest\");\n var xStripped = scope.o.xValue, dsl = '';\n scope.valid = true;\n if (scope.o.xType == 'string') {\n dsl = '\"' + scope.o.xValue + '\"';\n }\n else if (scope.o.xType == 'regex') {\n if (scope.o.xValue !== '')\n dsl = '\"' + scope.o.xValue + '\"';\n else\n scope.valid = false;\n }\n else if (scope.o.xType == 'number') {\n if (/^\\-?[0-9]+(\\.[0-9]+)?$/.test(scope.o.xValue))\n dsl = scope.o.xValue;\n else\n scope.valid = false;\n }\n else if ((scope.o.xType == 'field') || (scope.o.xType == 'subfield')) {\n if (scope.o.xValue && scope.o.xValue !== '')\n dsl = scope.o.xValue;\n else\n scope.valid = false;\n }\n else if (scope.o.xType == 'boolean') {\n if (typeof(scope.o.xValue) !== 'object') scope.o.xValue = {};\n if (scope.o.xValue._valid) {\n dsl = scope.o._dsl;\n xStripped = angular.copy(scope.o);\n //delete scope.o._dsl;\n //delete scope.o._valid;\n }\n else\n scope.valid = false;\n }\n else {\n scope.valid = false;\n }\n scope.i._valid = scope.valid;\n if (scope.valid) {\n scope.i._dsl = '(' + scope.o.op.replace('X',dsl) + ')';\n scope.i.op = scope.o.op;\n scope.i.xValue = xStripped;\n scope.i.xType= scope.o.xType;\n }\n\n };\n //scope.$on('mvr-register', function() {\n scope.checkValidity();\n\n var dereg = scope.$watch('o', function(newVal, oldVal) {\n if (!(newVal && !angular.equals(newVal, oldVal))) return;\n\n scope.checkValidity();\n }, true);\n scope.$on('$destroy', function() {\n //console.log(\"Subfield DEregister\");\n dereg();\n });\n //});\n }\n };\n})\n\n.directive('bvMvrInputMarc', function() {\n return {\n templateUrl: '/app/static/partials/staff/tools/mvr/inc/marc-input.html',\n restrict: 'E',\n scope: {\n x: '=model',\n withField: '@',\n withSubfield: '@',\n withSubfields: '@',\n skipAt: '@',\n },\n link: function(scope, elem, attr) {\n var m;\n\n var parseMarc = function(s) {\n // This could probably be done with a single regex but I am beyond fried right now\n if (scope.skipAt)\n s = '@' + s;\n if (typeof(s) !== 'string') {\n return false;\n }\n else if ((m = s.match(/^\\@([0-9.][0-9.][0-9.])\\|([0-9.#])([0-9.#])([$^])\\[([^\\]]+)\\]$/)) && scope.withField && scope.withSubfields) {\n return {tag: m[1], ind1: m[2], ind2: m[3], subType: m[4], sub: m[5]};\n }\n else if ((m = s.match(/^\\@([0-9.][0-9.][0-9.])\\|([0-9.#])([0-9.#])([$^])(.)$/)) && scope.withField && (scope.withSubfield || scope.withSubfields)) {\n return {tag: m[1], ind1: m[2], ind2: m[3], subType: m[4], sub: m[5]};\n }\n else if ((m = s.match(/^\\@([0-9.][0-9.][0-9.])([$^])\\[([^\\]]+)\\]$/)) && scope.withField && scope.withSubfields) {\n return {tag: m[1], ind1: '', ind2: '', subType: m[2], sub: m[3]};\n }\n else if ((m = s.match(/^\\@([0-9.][0-9.][0-9.])([$^])(.)$/)) && scope.withField && (scope.withSubfield || scope.withSubfields)) {\n return {tag: m[1], ind1: '', ind2: '', subType: m[2], sub: m[3]};\n }\n else if ((m = s.match(/^([$^])\\[([^\\]]+)\\]$/)) && !scope.withField && scope.withSubfields) {\n return {tag: '', ind1: '', ind2: '', subType: m[1], sub: m[2]};\n }\n else if ((m = s.match(/^([$^])(.)$/)) && !scope.withField && (scope.withSubfield || scope.withSubfields)) {\n return {tag: '', ind1: '', ind2: '', subType: m[1], sub: m[2]};\n }\n else if ((m = s.match(/^\\@([0-9.][0-9.][0-9.])\\|([0-9.#])([0-9.#])$/)) && scope.withField && !scope.withSubfields && !scope.withSubfield) {\n return {tag: m[1], ind1: m[2], ind2: m[3], subType: '', sub: ''};\n }\n else if ((m = s.match(/^\\@([0-9.][0-9.][0-9.])$/)) && scope.withField && !scope.withSubfields && !scope.withSubfield) {\n return {tag: m[1], ind1: '', ind2: '', subType: '', sub: ''};\n }\n else {\n return false;\n }\n };\n\n scope.v = {tag: '', ind1: '', ind2: '', sub: '', subType: '$'};\n var t = parseMarc(scope.x);\n if (t) {\n if (scope.withField) {\n scope.v.tag = t.tag;\n scope.v.ind1 = t.ind1;\n scope.v.ind2 = t.ind2;\n }\n if (scope.withSubfields) {\n scope.v.subType = t.subType;\n scope.v.sub = t.sub;\n }\n else if (scope.withSubfield) {\n scope.v.subType = '$';\n scope.v.sub = t.sub.substr(0,1);\n }\n }\n\n scope.$watch('v', function(newVal, oldVal) {\n if (!(newVal && oldVal && !angular.equals(newVal,oldVal))) return;\n\n var s = '';\n if (scope.withField) {\n if (!scope.skipAt)\n s = s + '@';\n s = s + scope.v.tag;\n if (scope.v.ind1 !== '' || scope.v.ind2 !== '') {\n s = s + '|' \n + (scope.v.ind1 !== '' ? scope.v.ind1 : '*')\n + (scope.v.ind2 !== '' ? scope.v.ind2 : '*');\n }\n }\n if (scope.withSubfields) {\n if ((scope.v.sub.length > 1) || (scope.v.sub == '['))\n s = s + scope.v.subType + '[' + scope.v.sub + ']';\n else\n s = s + scope.v.subType + scope.v.sub;\n }\n else if (scope.withSubfield) {\n s = s + scope.v.subType + scope.v.sub.substr(0,1);\n }\n\n if (parseMarc(s)) {\n scope.x = s;\n scope.valid = true;\n }\n else {\n scope.x = false;\n scope.valid = false;\n }\n }, true);\n\n }\n };\n})\n;\n\n})();\n","(function () {\n \"use strict\";\n\nvar module = angular.module('koha.validSession', [])\n.factory('kohaHttpResendQueue', [\"$injector\", function($injector){\n var queue = [];\n var $http; // to avoid circular dep.\n\n return {\n enqueue : function(config, deferred){\n queue.push({\n config: config,\n deferred: deferred\n });\n },\n resendAll : function(){\n $http = $http || $injector.get('$http');\n var successCB = function(r){ console.log(r); request.deferred.resolve(r); return request.deferred.promise; };\n var errorCB = function(r){ console.log(r); request.deferred.reject(r); return request.deferred.promise; };\n\n for(var i=0; i 1) {\n angular.forEach(paths[1].split('&'), function(p) { args.push(p); });\n }\n // note: only gets `:param` syntax, misses `{param:type}` and others.\n paths[0].replace(/\\/:\\w+/g, function(p) { args.push(p.substring(2)); });\n\n return args;\n };\n\n var makeIframe = function(script, attrs, $stateParams, params) {\n var attrsStr = (attrs||[]).join(' ');\n if (script == '/acqui/getit.pl') {\n attrsStr = attrsStr + ' height-tracks-header';\n }\n var s = '';\n // console.warn(s);\n return s;\n };\n\n this.widget = function(widgetName, widgetDef) {\n return this.appState(widgetName, {\n title: widgetDef.title,\n permissions: widgetDef.permissions,\n widget: widgetDef,\n registerState: false,\n matchRoute: false\n });\n };\n\n this.appState = function(appName, appDef) {\n\n var stateParameters = parseUrlArgs(appDef.url || '');\n\n var matches = appName.match(/^(.+)\\./);\n if (matches && matches.length == 2) {\n var parentState = matches[1];\n if (this.appParameters[parentState]) {\n angular.forEach(this.appParameters[parentState], function(p) {\n stateParameters.push(p);\n });\n }\n }\n\n this.appParameters[appName] = stateParameters;\n\n\n // ui-router state definition\n if (appDef.registerState !== false) {\n var permissions = appDef.permissions || {catalogue: {access: '*'}};\n var title = appDef.title || 'Staff App';\n\n var stateDef = {\n url: appDef.url,\n data: {\n pageSubTitle: title\n },\n resolve: {\n base: function() { return {} },\n checkPermissions: [\"userService\", function(userService) {\n return userService.whenAuthenticatedUserCan(permissions, null, {redirectOnFail: '/'});\n }]\n }\n };\n\n if (appDef.resolve)\n angular.extend(stateDef.resolve, appDef.resolve);\n\n if (appDef.controller)\n stateDef.controller = appDef.controller;\n\n if (appDef.params)\n stateDef.params = appDef.params;\n\n if (appDef.reloadOnSearch)\n stateDef.reloadOnSearch = appDef.reloadOnSearch;\n\n if (appDef.templateProvider) {\n stateDef.templateProvider = appDef.templateProvider;\n }\n else if (appDef.templateUrl) {\n stateDef.templateUrl = '/app/static/partials/staff/' + appDef.templateUrl;\n }\n else if (appDef.template) {\n stateDef.template = appDef.template;\n }\n else if (appDef.legacyUrl) {\n stateDef.templateProvider = [\"$stateParams\", function($stateParams) {\n //console.log(\"STATE PARAMS =\");\n //console.dir(stateParameters);\n return makeIframe(appDef.legacyUrl, appDef.iframeAttrs, $stateParams, stateParameters);\n }];\n }\n else if (appDef.modal) {\n // ...\n }\n else {\n throw \"staffLegacyAppProvider \" + appName + \": app must provide a templateProvider, templateUrl, template, legacyUrl, or declare modal=true\";\n }\n $stateProvider.state(\"staff.\"+appName, stateDef);\n }\n\n // widget and menu\n if (appDef.widget) {\n var widgetDef = angular.extend({}, {\n state: \"staff.\" + appName,\n title: appDef.title,\n permissions: appDef.permissions,\n outerClass: 'staff-widget-outer',\n }, appDef.widget);\n\n widgetDef.addToSidebar = (widgetDef.addToSidebar === false ? false : true);\n widgetDef.widgetOuterClass = widgetDef.widgetOuterClass || 'staff-widget-outer';\n\n self.widgets[\"staff.\" + appName] = widgetDef;\n\n var base = appName.replace(/\\..*/, '');\n //console.log(\"Appname \" + appName + \" => base \" + base);\n if (!self.widgetGroup[base])\n self.widgetGroup[base] = [];\n self.widgetGroup[base].push(widgetDef);\n //console.dir(self.widgetGroup);\n\n if (appDef.widget.addToDock !== false)\n self.widgetDock.push(\"staff.\" + appName);\n }\n\n // route matcher\n if (appDef.matchRoute) {\n var path;\n if (typeof(appDef.matchRoute) == 'string') {\n registerRouteMatcher(appDef.matchRoute, {path: appDef.matchRoute, state: 'staff.'+appName});\n }\n else if (typeof(appDef.matchRoute) == 'object') {\n registerRouteMatcher(appDef.matchRoute.path, appDef.matchRoute);\n }\n\n else if (typeof(appDef.matchRoute) == 'function') {\n if (!appDef.legacyUrl) {\n throw \"staffLegacyAppProvider \" + appName + \": function matchRoute must also provide legacyUrl\";\n }\n path = '/bvcgi' + appDef.legacyUrl;\n registerRouteMatcher(path, {\n priority: 10,\n state: 'staff.'+appName,\n searchFilter: appDef.matchRoute,\n stateParameters: stateParameters,\n });\n }\n\n else if (appDef.legacyUrl && stateParameters.length) {\n path = '/bvcgi' + appDef.legacyUrl.replace(/\\?.*/,'');\n\n registerRouteMatcher(path, {\n priority: 10,\n state: 'staff.'+appName,\n searchFilter: function() { return true; },\n stateParameters: stateParameters\n });\n }\n else if (appDef.legacyUrl) {\n path = '/bvcgi' + appDef.legacyUrl.replace(/\\?.*/,'');\n\n registerRouteMatcher(path, {\n priority: 0,\n state: 'staff.'+appName\n });\n }\n else {\n throw \"staffLegacyAppProvider \" + appName + \": matchRoute requires explicit matcher object or legacyUrl\";\n }\n }\n else if (appDef.matchRoute !== false) {\n throw \"staffLegacyAppProvider \" + appName + \": matchRoute is required\";\n }\n\n if (!this.defaultRouteEntered) {\n this.defaultRouteEntered = true;\n \n $stateProvider.state('staff.default', {\n url: '/default?path&search',\n data: { pageSubTitle: 'Staff Application' },\n resolve: {\n base: [\"resolvedPermissions\", function(resolvedPermissions) { return {} }],\n checkPermissions: [\"userService\", function(userService) {\n return userService.whenAuthenticatedUserCan({catalogue: {access: '*'}}, null, {redirectOnFail: '/'});\n }]\n },\n //controller: something,\n templateProvider: [\"$stateParams\", function($stateParams) {\n // Double encoded due to ui-router bug\n var decodedPath = $stateParams.path.replace(/\\%2F/, '/');\n var s = '
You have reached a legacy staff application which '\n + 'has not been registered in the route table. The legacy app path is '\n + $stateParams.path + ' and the query arguments are ' + $stateParams.search + '
'\n + '';\n return s;\n }]\n });\n\n }\n\n return self;\n };\n\n\n this.$get = [\"$state\", \"$location\", \"$rootScope\", \"$timeout\", \"staffRedirectService\", function staffLegacyAppService($state, $location, $rootScope, $timeout, staffRedirectService) {\n var svc = {};\n\n svc.getWidgets = function(module) {\n return self.widgetGroup[module];\n };\n\n svc.getWidgetSidebar = function(module) {\n var widgets = self.widgetGroup[module];\n return jQuery.grep(widgets, function(w) { return (w.addToSidebar !== false); });\n };\n\n svc.getWidgetGroup = function(module, group) {\n var widgets = self.widgetGroup[module];\n return jQuery.grep(widgets, function(w) { return (w.group == group) });\n };\n\n svc.getWidgetGroups = function(module) {\n var widgets = self.widgetGroup[module];\n //console.log(\"Get widget groups\");\n //console.dir(widgets);\n var grouped = {};\n angular.forEach(widgets, function(w) {\n if (!grouped[w.group])\n grouped[w.group] = [];\n grouped[w.group].push(w);\n });\n return grouped;\n };\n\n svc._matchDefaultRoute = function(evnt, doStateChange) {\n var path = evnt.path;\n if (!path.match(/^\\/cgi-bin\\/koha/))\n return false;\n\n path = path.replace(/^\\/cgi-bin\\/koha\\//, '');\n\n // We shouldn't have to do this, but apparently there's a subtle bug in ui-router which double-encodes slashes\n path = encodeURIComponent(path);\n\n //console.dir(path);\n //console.dir(evnt.search);\n var newParams = {search: evnt.search, path: path};\n //console.log(\"Comparing\");\n //console.dir(newParams);\n //console.dir($state.params);\n\n if (('staff.default' != $state.current.name) || !angular.equals(newParams, ($state.params||{}))) {\n if (doStateChange) {\n //console.log(\"================================\");\n //console.dir(angular.copy(path));\n $state.go('staff.default', newParams, {inherit: false, lossy: false});\n //console.dir(angular.copy($state.params));\n //console.log(\"=------------------------------=\");\n }\n else {\n var url = $state.href('staff.default', {search: evnt.search, path: path}, {inherit: false, lossy: false});\n //console.log(\"Silent transition from \" + $location.url() + \" to url=\" + url);\n if (/batch\\-manager.*op=add\\&/.test(url)) {\n console.log(\"Suppressing rewrite for batch-manager mutating GET\");\n }\n else {\n staffRedirectService.rewriteLocation(url);\n }\n }\n return true;\n } else if (doStateChange) {\n $state.reload();\n }\n else {\n //console.log(\"No state change, and no need to update history\");\n }\n };\n\n // Route matcher. If we match a registered route, change state and return true.\n svc.matchRoute = function(evnt, doStateChange, withKludge) {\n if (!svc.routeMatcherSorted) {\n angular.forEach(svc.routeMatcher, function(def, key) {\n svc.routeMatcher[key] = def.sort(function(a,b) { return (b.priority - a.priority) });\n });\n svc.routeMatcherSorted = true;\n }\n\n var matches = self.routeMatcher[evnt.path];\n\n if (!matches)\n return svc._matchDefaultRoute(evnt, doStateChange);\n\n var queryParams = {};\n if (evnt.search) {\n //should handle params delimited by semicolon,as is the case with CGI's url()\n var components = evnt.search.substr(1).split(/&|;/);\n angular.forEach(components, function(c) {\n var fv = c.split('=');\n queryParams[fv[0]] = decodeURIComponent(fv[1]);\n });\n }\n\n var matched;\n\n function cleanParams (params) {\n var newParams = {};\n Object.keys( params ).forEach(function(p){\n if( p != '#' && params[p] !== undefined)\n newParams[p] = params[p];\n });\n return newParams;\n }\n\n for (var i=0; i \" + matched.state);\n console.log(\"Update params: \" + JSON.stringify(stateParams) + \" => \" + JSON.stringify(matched.params));\n // Force state change if we need to change the staff sidebar\n var s1 = $state.current.name.split('.');\n var s2 = matched.state.split('.');\n if (s1[0] != s2[0] || s1[1] != s2[1])\n doStateChange = true;\n\n if (doStateChange) {\n // console.log(\"Explicit state change to \" + matched.state + \" with params...\");\n // console.dir(matched.params);\n // console.log($state.href(matched.state, matched.params, {inherit: false}));\n $state.go(matched.state, matched.params, {inherit: false});\n }\n else {\n // if current state PATH params are missing, restore them.\n // this can happen on POSTs (specifically circulation.pl)\n if(matched.state == $state.current.name){\n angular.forEach(stateParams, function(paramval, param){\n if((!$state.$current.params[param] || $state.$current.params[param].location == 'path') && !matched.params[param] ){\n console.warn(\"restoring path param \" + param + \" as \" + paramval);\n matched.params[param] = paramval; // might be preferable to just not do transition ?\n }\n });\n }\n var paramMap = {};\n angular.forEach(matched.params, function(value,key) { paramMap[key] = value; });\n // console.dir(paramMap);\n var url = $state.href(matched.state, paramMap, {inherit: false, lossy: false});\n console.log(\"Silent transition from \" + $location.url() + \" in state \" + matched.state + \" to url=\" + url);\n if (/batch\\-manager.*op=add\\&/.test(url)) {\n console.log(\"Suppressing rewrite for batch-manager mutating GET\");\n }\n else if (/patron\\-lists\\-bulkedit\\-confirm/.test(url)) {\n console.log(\"Suppressing rewrite for patron-lists-bulkedit-confirm GET\");\n }\n else if (/\\/app\\/staff\\/lists\\?display\\=privateshelves/.test($location.url())) {\n console.log('Suppressing rewrite for /app/staff/lists?display=privateshelves');\n }\n else if (/\\/app\\/staff\\/patrons\\/bulk-edit\\?param_borrower_list\\=/.test(url)) {\n console.log('Suppressing rewrite for /app/staff/patron/bulk-edit?patram_borrower_list=...');\n }\n else if (/\\/app\\/staff\\/bib\\/\\d+\\/merge\\?confirm_bib_merge\\=Accept/.test(url)) {\n console.log('Suppressing rewrite for /app/staff/bib/.../merge?confirm_bib_merge=Accept...');\n }\n else {\n staffRedirectService.rewriteLocation(url);\n }\n }\n } else if (doStateChange) {\n $state.reload();\n }\n else {\n console.log(\"No state change, and no need to update history\");\n }\n return true;\n }\n }\n\n if (!withKludge && evnt.search.match(/\\&/)) {\n console.log(\"========== Repeating match attempt with kludge =========\");\n var newSearch = evnt.search.replace(/\\&/g,'&');\n return svc.matchRoute({path:evnt.path, search: newSearch}, doStateChange, true);\n }\n return svc._matchDefaultRoute(evnt, doStateChange);\n };\n\n return svc;\n }];\n\n return this;\n}])\n\n.factory('staffRedirectService', [\"$location\", \"$timeout\", function($location, $timeout) {\n var svc = {\n tickets: {},\n ticketCount: 0,\n debug: false\n };\n\n\n svc.getTicket = function(mask) {\n if (!mask) mask=1;\n svc.ticketCount++;\n svc.tickets[svc.ticketCount] = mask;\n return svc.ticketCount;\n };\n\n svc.consumeTicket = function(ticketId, mask) {\n if (!mask) mask=1;\n if (!(ticketId in svc.tickets))\n return false;\n if ((svc.tickets[ticketId] & mask) != mask) {\n delete svc.tickets[ticketId];\n return false;\n }\n svc.tickets[ticketId] = svc.tickets[ticketId] & (~mask);\n if (svc.tickets[ticketId] == 0)\n delete svc.tickets[ticketId];\n\n return true;\n };\n\n svc.hash = function(s) {\n return murmurhash3_32_gc(s,0);\n };\n\n svc.suppressLocationChange = function(cur,next) {\n //console.log(\"SPR: attempt to suppress location \" + cur + \" => \" + next);\n\n var m = cur.match(/_spr=(\\d+)\\.([^\\&]+)/);\n if (!m) {\n //console.log(\"SPR: false (no URI match)\");\n return false;\n }\n\n var ticket = m[1];\n var referer = decodeURIComponent(m[2]);\n\n if (!svc.consumeTicket(ticket, 2)) {\n //console.log(\"SPR: false (ticket already consumed)\");\n return false;\n }\n\n if (referer == svc.hash($location.url(),0)) {\n //console.log(\"SPR: true (hash match)\");\n return true;\n }\n else {\n //console.log(\"SPR: false (hash mismatch)\");\n return false;\n }\n };\n\n svc.suppressStateChange = function(fromState, fromParams, toState, toParams) {\n if(svc.debug) console.log(\"SPR: attempt to suppress state \" + fromState.name + ':' + JSON.stringify(fromParams)\n + \" => \" + toState.name + ':' + JSON.stringify(toParams));\n\n var url = $location.url();\n var m = url.match(/_spr=(\\d+)\\.([^\\&]+)/);\n if (!m) {\n if(svc.debug) console.log(\"SPR: false (no URI match)\");\n return false;\n }\n\n var ticket = m[1];\n if (svc.consumeTicket(ticket, 1)) {\n if (!angular.equals(fromParams,toParams)) {\n if(svc.debug) console.log(\"SPR: false (parameter mismatch)\");\n return false;\n } else {\n if(svc.debug) console.log(\"SPR: true (ticket OK)\");\n return true;\n }\n }\n else {\n if(svc.debug) console.log(\"SPR: false (ticket already consumed)\");\n return false;\n }\n };\n\n svc.rewriteLocation = function(url) {\n if(svc.debug) console.log(\"RL URL = \" + url);\n\n url = url + (url.match(/\\?/) ? '&' : '?');\n var ticket = svc.getTicket(3);\n\n var referer = encodeURIComponent(svc.hash($location.url()));\n\n url = url + '_spr=' + ticket + '.' + referer;\n if(svc.debug) console.log(\"NEW URL + \" + url);\n // return;\n\n $timeout(function() {\n if(svc.debug) console.log(\"SPR: pushing state \" + url);\n if (window.history && window.history.replaceState)\n window.history.replaceState({postRoute: true}, \"Transient POST state\", url);\n else\n History.replaceState({postRoute: true}, \"Transient POST state\", url);\n\n }, 0);\n };\n\n return svc;\n}])\n\n\n// This is mostly taken from angular ngInclude source, which it resembles\n// Note the use of two functions on the same directive\n\n.directive('staffWidget', [\"$sce\", \"$templateRequest\", \"$animate\", function($sce, $templateRequest, $animate) {\n return {\n priority: 400,\n terminal: true,\n transclude: 'element',\n controller: angular.noop,\n compile: function(elem, attr) {\n var widgetExp = attr.staffWidget;\n\n return function(scope, $element, $attr, ctrl, $transclude) {\n var changeCounter = 0,\n currentScope,\n previousElem,\n currentElem;\n\n // Maybe not necessary in our use case\n var cleanupLastIncludeContent = function() {\n if (previousElem) {\n previousElem.remove();\n previousElem = null;\n }\n if (currentScope) {\n currentScope.$destroy();\n currentScope = null;\n }\n if (currentElem) {\n $animate.leave(currentElem).then(function() {\n previousElem = null;\n });\n previousElem = currentElem;\n currentElem = null;\n }\n };\n\n scope.$watch(widgetExp, function(widget) {\n var thisChangeId = ++changeCounter;\n if (!widget) {\n //console.log(\"Request for unrecognized staff widget \" + scope.$eval('w'));\n return;\n }\n\n var titleClass = widget.titleClass || 'staff-widget-title';\n if (widget.templateUrl) {\n $templateRequest($sce.trustAsResourceUrl(widget.templateUrl)).then(function(resp) {\n var newScope = scope.$new();\n if (widget.addTitle) {\n ctrl.template = '
{{' + widgetExp + '.title}}
' + resp;\n }\n else {\n ctrl.template = resp;\n }\n \n // See comment about scope pollution in angular ngInclude source\n var clone = $transclude(newScope, function(clone) {\n cleanupLastIncludeContent();\n $animate.enter(clone, null, $element);\n });\n currentScope = newScope;\n currentElem = clone;\n \n //currentScope.$emit($includeContentLoaded, widget.templateUrl);\n }, function() {\n if (thisChangeId === changeCounter) {\n cleanupLastIncludeContent();\n }\n });\n }\n else {\n var newScope = scope.$new();\n var tmpl = widget.template || \n ''\n + '
{{' + widgetExp + '.description}}
';\n if (widget.addTitle) {\n ctrl.template = '
{{' + widgetExp + '.title}}
' + tmpl;\n }\n else {\n ctrl.template = tmpl;\n }\n var clone = $transclude(newScope, function(clone) {\n cleanupLastIncludeContent();\n $animate.enter(clone, null, $element);\n });\n currentScope = newScope;\n currentElem = clone;\n }\n });\n };\n }\n };\n}])\n\n.directive('staffWidget', [\"$compile\", function ($compile) {\n return {\n priority: -400,\n require: 'staffWidget',\n link: function(scope, $element, $attr, ctrl) {\n if (/SVG/.test($element[0].toString())) {\n // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698\n // support innerHTML, so detect this here and try to generate the contents\n // specially.\n $element.empty();\n $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,\n function namespaceAdaptedClone(clone) {\n $element.append(clone);\n }, {futureParentElement: $element});\n return;\n }\n\n $element.html(ctrl.template);\n $compile($element.contents())(scope);\n }\n };\n}])\n\n.filter('propsFilter', function() {\n return function(items, props) {\n var out = [];\n if (angular.isArray(items)) {\n items.forEach(function(item) {\n var itemMatches = false;\n var keys = Object.keys(props);\n for (var i=0; i\") + wrap[2];\n\n // Descend through wrappers to the right content\n i = wrap[0];\n while (i--) {\n tmp = tmp.lastChild;\n }\n\n nodes = concat(nodes, tmp.childNodes);\n\n tmp = fragment.firstChild;\n tmp.textContent = \"\";\n }\n\n // Remove wrapper from fragment\n fragment.textContent = \"\";\n fragment.innerHTML = \"\"; // Clear inner HTML\n forEach(nodes, function(node) {\n fragment.appendChild(node);\n });\n\n return fragment;\n}\n \n\n// XXX - we chould probably push an intermediate warning state after a POST operation\n\n/*\nwindow.addEventListener('popstate', function(event) {\n if (event.state && event.state.postRoute) {\n // do something useful\n }\n}, false);\n*/\n\n})();\n","(function () {\n'use strict';\n\nangular.module('kohastaff.apps', []).config([\"staffLegacyAppProvider\", \"$stateProvider\", function(staffLegacyAppProvider, $stateProvider) {\n \n staffLegacyAppProvider\n\n //================= Admin ===============\n .appState('admin', {\n title: 'Administration',\n permissions: {parameters: '*'},\n url: '/admin',\n templateUrl: 'admin.html',\n controller: 'StaffAdministrationCtrl',\n matchRoute: {\n path: '/bvcgi/admin/admin-home.pl',\n },\n widget: {\n addToSidebar: false\n }\n })\n .appState('admin.sysprefs', {\n title: 'Global System Preferences',\n permissions: {parameters: 'syspref'},\n url: '/sysprefs?tab&op',\n legacyUrl: '/admin/systempreferences.pl',\n matchRoute: function(params) {\n return ('searchfield' in params ? false : true);\n },\n widget: {\n group: 'global',\n description: 'Manage global system preferences like MARC flavor, date format, administrator email, etc.',\n }\n })\n .appState('admin.sysprefs-search', {\n title: 'Search System Preferences',\n permissions: {parameters: 'syspref'},\n url: '/sysprefs/:searchfield?tab&op',\n legacyUrl: '/admin/systempreferences.pl',\n matchRoute: function(params) {\n return ('searchfield' in params ? true : false);\n },\n widget: {\n group: 'global',\n outerClass: 'staff-widget-outer staff-widget-outer-bounded clearfix col-sm-12',\n templateUrl: '/app/static/partials/staff/tabs/sysprefSearchTab.html',\n addToSidebar: false,\n }\n })\n .appState('admin.branches', {\n title: 'Libraries and Groups',\n permissions: {parameters: {branch: '*'}},\n url: '/branches?op&branchcode&branchname&categorycode', \n legacyUrl: '/admin/branches.pl',\n matchRoute: true,\n widget: {\n group: 'basic',\n description: 'Define libraries and groups',\n }\n })\n .widget('admin.vendors', {\n title: 'Vendors',\n permissions: {parameters: {branch: '*'}},\n group: 'basic',\n description: 'Define vendors',\n displayIf: function() { return KOHA.config.AcqLists; },\n\n //registerState: false,\n //matchRoute: false,\n //widget: {}\n })\n .appState('admin.itemtypes', {\n title: 'Item Types',\n permissions: {parameters: 'item'},\n url: '/itemtypes?op&itemtype&page',\n legacyUrl: '/admin/itemtypes.pl',\n matchRoute: true,\n widget: {\n group: 'basic',\n description: 'Define item types used for circulation rules',\n }\n })\n .appState('admin.itemfields', {\n title: 'Item Fields',\n permissions: {parameters: 'item'},\n url: '/itemfields',\n controller: 'StaffAdminItemFieldsCtrl',\n templateUrl: 'itemfields.html',\n matchRoute: false,\n widget: {\n group: 'basic',\n description: 'Define custom item-level data',\n }\n })\n .appState('admin.marcdisplaytmpl', {\n title: 'MARC Display Templates',\n permissions: 'parameters',\n url: '/marc-display-tmpl',\n controller: 'StaffMarcDisplayTmplCtrl',\n templateUrl: 'admin/marc-display-tmpl.html',\n matchRoute: false,\n widget: {\n group: 'catalog',\n description: 'Create templates that define how MARC data is displayed; ' +\n 'Assign templates to control search results and detail views.',\n }\n })\n .appState('admin.itemstatusconfig', {\n title: 'Custom Item Statuses',\n permissions: {parameters: 'item'},\n url: '/itemstatusconfig',\n controller: 'StaffAdminItemStatusConfigCtrl',\n templateUrl: 'itemstatusconfig.html',\n matchRoute: false,\n widget: {\n group: 'basic',\n description: 'Define custom item statuses that affect circulation ',\n }\n })\n .appState('admin.accounttypes', {\n title: 'Invoice Account Types',\n permissions: {parameters: 'invoice'},\n url: '/accounttypes',\n controller: 'StaffAdminAccountTypesCtrl',\n templateUrl: 'admin/accounttypes-config.html',\n matchRoute: false,\n widget: {\n group: 'circ',\n description: 'Define invoice / fee types and parameters',\n }\n })\n .appState('admin.categories', {\n title: 'Patron Categories',\n permissions: {parameters: 'patron'},\n url: '/categories?op&categorycode',\n legacyUrl: '/admin/categorie.pl',\n matchRoute: true,\n widget: {\n group: 'patrons',\n description: 'Define patron categories',\n }\n })\n .appState('admin.cities', {\n title: 'Cities and Towns',\n permissions: {parameters: 'location'},\n url: '/cities?op&city_name&city_zipcode&cityid',\n legacyUrl: '/admin/cities.pl',\n matchRoute: true,\n widget: {\n group: 'patrons',\n description: 'Define cities and towns that your patrons live in',\n }\n })\n .appState('admin.roadtype', {\n title: 'Road Types',\n permissions: {parameters: 'location'},\n url: '/roadtype?op&roadtypeid&road_type',\n legacyUrl: '/admin/roadtype.pl',\n matchRoute: true,\n widget: {\n group: 'patrons',\n description: 'Define road types (street, avenue, way, etc.). Road types display as authorized values when adding/editing patrons and can be used in geographic statistics',\n }\n })\n .appState('admin.patron-attr-types', {\n title: 'Patron Attribute Types',\n permissions: {parameters: 'patron'},\n url: '/patron-attr-types?op&code',\n legacyUrl: '/admin/patron-attr-types.pl',\n matchRoute: true,\n widget: {\n group: 'patrons',\n description: 'Define extended attributes (identifiers and statistical categories) for patron records',\n }\n })\n .widget('admin.circ.rules', {\n title: 'Circulation Policies',\n permissions: {parameters: {circ_policy: '*'}},\n group: 'circ',\n description: 'Manage circulation/fines policies, rules, recall rules, and terms',\n })\n .appState('admin.circ-exceptions', {\n title: 'Circulation Exceptions',\n permissions: {parameters: 'circ_exception'},\n url: '/circ-exceptions?op',\n legacyUrl: '/admin/circ_exceptions.pl',\n matchRoute: true,\n widget: {\n group: 'circ',\n description: 'Manage behavior when circulation exceptions arise',\n }\n })\n .appState('admin.transfer-limits', {\n title: 'Library Transfer Limits',\n permissions: {parameters: {transfer_limit: '*'}},\n url: '/transfer-limits',\n legacyUrl: '/admin/branch_transfer_limits.pl',\n matchRoute: true,\n widget: {\n group: 'circ',\n description: 'Limit the ability to transfer items between libraries based on the library sending, the library receiving, and the item type involved. These rules only go into effect if the preference UseBranchTransferLimits is set to ON.',\n }\n })\n .appState('admin.item-circ-alerts', {\n title: 'Item Circulation Alerts',\n permissions: {parameters: {circ_alert: '*'}},\n url: '/item-circ-alerts?id&branch&action',\n legacyUrl: '/admin/item_circulation_alerts.pl',\n matchRoute: true,\n widget: {\n group: 'circ',\n description: 'Define rules for check-in and checkout notifications for combinations of libraries, patron categories, and item types',\n }\n })\n .appState('admin.fine-threshold-rules', {\n title: 'Fine Threshold Rules',\n permissions: {parameters: {fine_rule: '*'}},\n url: '/fine-threshold-rules?id&branch&branchcode&op&type',\n legacyUrl: '/admin/fine-threshold-rules.pl',\n matchRoute: true,\n widget: {\n group: 'circ',\n description: 'Set up thresholds to forgive fines based on assigned parameters.',\n }\n })\n .appState('admin.authvals', {\n title: 'Authorized Values',\n permissions: {parameters: 'authval'},\n url: '/authvals?op&id&category&searchfield&page',\n legacyUrl: '/admin/authorised_values.pl',\n matchRoute: true,\n widget: {\n group: 'catalog',\n description: 'Define categories and authorized values for them.',\n }\n })\n .appState('admin.class-sources', {\n title: 'Classification Sources',\n permissions: {parameters: 'cataloging'},\n url: '/class-sources?op&sort_rule&class_source',\n legacyUrl: '/admin/classsources.pl',\n matchRoute: true,\n widget: {\n group: 'catalog',\n description: 'Define classification sources (i.e., call number schemes) used by your collection. Also define filing rules used for sorting call numbers.',\n }\n })\n .appState('admin.matching-rules', {\n title: 'Record Matching Rules',\n permissions: {parameters: 'cataloging'},\n url: '/matching-rules.pl?op&matcher_id',\n legacyUrl: '/admin/matching-rules.pl',\n matchRoute: true,\n widget: {\n group: 'catalog',\n description: 'Manage rules for automatically matching MARC records during record imports.',\n }\n })\n .appState('admin.z3950-targets', {\n title: 'Z39.50 Client Targets',\n permissions: {parameters: 'netsvc'},\n url: '/z3950-targets?op&offset&searchfield',\n legacyUrl: '/admin/z3950servers.pl',\n matchRoute: true,\n widget: {\n group: 'additional',\n description: 'Define which servers to query for MARC data in the integrated Z39.50 client.',\n }\n })\n .widget('admin.ncip.agencies', {\n title: 'NCIP Config',\n permissions: {parameters: {netsvc: '*'}},\n group: 'additional',\n description: 'Manage NCIP configuration',\n })\n .widget('admin.cron.index', {\n title: 'Cron Scripts',\n permissions: {admin: 'cron_jobs'},\n description: 'Manage system cron scripts',\n group: 'additional',\n })\n .widget('admin.appconfig', {\n title: 'Discovery Layer Configuration',\n permissions: { admin: '*' },\n description: 'Configure Discovery Layer interface',\n group: 'additional'\n })\n //================= Reports ===============\n \n .appState('reports', {\n title: 'Reports',\n permissions: {reports: '*'},\n url: '/reports',\n templateUrl: 'reports.html',\n controller: 'StaffReportsCtrl',\n matchRoute: {\n path: '/bvcgi/admin/reports-home.pl',\n },\n widget: {\n addToSidebar: false\n }\n })\n .widget('reports.sql.index', {\n title: 'SQL Reports',\n permissions: {reports: 'run'},\n group: 'guided',\n description: 'Manage or run guided and SQL reports',\n })\n .widget('reports.sql.new', {\n title: 'New SQL Report',\n permissions: {reports: 'run'},\n group: 'guided',\n description: 'Manage or run guided and SQL reports',\n })\n .appState('reports.guided-build', {\n title: 'Guided Reports Wizard',\n url: '/guided-build?phase&reports&tag&specify_parameters&specified_param&areas&order_by&page&id&submit',\n permissions: {reports: 'run'},\n legacyUrl: '/reports/guided_reports.pl',\n matchRoute: true,\n widget: {\n group: 'guided',\n description: 'Build a new guided report',\n }\n })\n .appState('reports.dictionary', {\n title: 'Dictionary',\n url: '/dictionary.pl?phase&id',\n legacyUrl: '/reports/dictionary.pl',\n permissions: 'reports',\n matchRoute: true,\n widget: {\n group: 'dictionary',\n description: 'Use the dictionary to define custom criteria for reporting',\n }\n })\n .appState('reports.patron-stats', {\n title: 'Patron Stats',\n url: '/patron-stats',\n legacyUrl: '/reports/borrowers_stats.pl',\n permissions: 'reports',\n matchRoute: true,\n widget: {\n group: 'stats',\n description: 'Generate statistics about patrons',\n }\n })\n .appState('reports.catalog-stats', {\n title: 'Catalog Stats',\n url: '/catalog-stats',\n legacyUrl: '/reports/catalogue_stats.pl',\n permissions: 'reports',\n matchRoute: true,\n widget: {\n group: 'stats',\n description: 'Generate statistics about catalog records',\n }\n })\n .appState('reports.circ-stats', {\n title: 'Circulation Stats',\n url: '/circ-stats',\n legacyUrl: '/reports/issues_stats.pl',\n permissions: 'reports',\n matchRoute: true,\n widget: {\n group: 'stats',\n description: 'Generate statistics about item circulation',\n }\n })\n /* FIXME - need a replacement report\n .appState('reports.serials-stats', {\n title: 'Serials Stats',\n url: '/serials-stats',\n legacyUrl: '/reports/serials_stats.pl',\n permissions: 'reports',\n matchRoute: true,\n widget: {\n group: 'stats',\n description: 'Generate statistics about serials subscriptions',\n }\n }) */\n .appState('reports.issues-avg-stats', {\n title: 'Average Loan Time',\n url: '/issues-avg-stats',\n legacyUrl: '/reports/issues_avg_stats.pl',\n permissions: 'reports',\n matchRoute: true,\n widget: {\n group: 'stats',\n description: 'Generate statistics about average checkout periods',\n }\n })\n .appState('reports.inactive-patrons', {\n title: 'Inactive Patrons',\n url: '/inactive-patrons',\n legacyUrl: '/reports/borrowers_out.pl',\n permissions: 'reports',\n matchRoute: true,\n widget: {\n group: 'inactive',\n description: 'Find patrons with no checkouts',\n }\n })\n .appState('reports.inactive-items', {\n title: 'Inactive Items',\n url: '/inactive-items',\n legacyUrl: '/reports/catalogue_out.pl',\n permissions: 'reports',\n matchRoute: true,\n widget: {\n group: 'inactive',\n permissions: 'reports',\n }\n })\n .appState('reports.patron-issues-top', {\n title: 'Most Active Patrons',\n url: '/patron-issues-top',\n legacyUrl: '/reports/bor_issues_top.pl',\n permissions: 'reports',\n matchRoute: true,\n widget: {\n group: 'top',\n description: 'Find patrons with the most checkouts',\n }\n })\n .appState('reports.item-issues-top', {\n title: 'Most Circulated Items',\n url: '/item-issues-top',\n legacyUrl: '/reports/cat_issues_top.pl',\n permissions: 'reports',\n matchRoute: true,\n widget: {\n group: 'top',\n description: 'Find the most circulated items',\n }\n })\n .appState('reports.items-lost', {\n title: 'Items Lost',\n url: '/items-lost',\n legacyUrl: '/reports/itemslost.pl',\n permissions: 'reports',\n matchRoute: true,\n widget: {\n group: 'other',\n description: 'Find items marked as lost',\n }\n })\n .appState('reports.catalog-by-itemtype', {\n title: 'Catalog by Item Type',\n url: '/catalog-by-itemtype?report_name',\n legacyUrl: '/reports/manager.pl?report_name=itemtypes',\n permissions: 'reports',\n matchRoute: true,\n widget: {\n group: 'other',\n description: 'View catalog group by item types',\n }\n })\n .appState('reports.nostate-reports-lib', {\n title: 'LibLime Reports Library',\n matchRoute: false,\n registerState: false,\n widget: {\n template: '',\n group: 'resources',\n addToSidebar: false, // Added explicitly by the sidebar template\n displayIf: function() { return KOHA.config.useExternalCDNs; },\n }\n })\n .appState('reports.nostate-mysql', {\n title: 'MySQL Documentation',\n matchRoute: false,\n registerState: false,\n widget: {\n template: '',\n group: 'resources',\n addToSidebar: false, // Added explicitly by the sidebar template\n displayIf: function() { return KOHA.config.useExternalCDNs; },\n }\n })\n \n // No widgets, but present for route matching purposes\n .appState('reports.manager', {\n title: 'Reports Manager',\n url: '/manager?report_name',\n legacyUrl: '/reports/manager.pl',\n permissions: 'reports',\n matchRoute: function(params) { \n return ((params.report_name != 'itemtypes') ? true : false);\n },\n widget: false\n })\n\n .appState('reports.stats-screen', {\n title: 'Reports Stats Screen',\n url: '/stats-screen?time&time2',\n legacyUrl: '/reports/stats.screen.pl',\n permissions: 'reports',\n matchRoute: true,\n widget: false,\n })\n .appState('reports.stats-print', {\n title: 'Reports Stats Print',\n url: '/stats-print?time&time2',\n legacyUrl: '/reports/stats.print.pl',\n permissions: 'reports',\n matchRoute: true,\n widget: false,\n })\n .appState('reports.legacy-acq-stats', {\n title: 'Legacy Acquisitions Stats',\n url: '/legacy-acq-stats',\n legacyUrl: '/reports/acquisitions_stats.pl',\n permissions: 'reports',\n matchRoute: true,\n widget: false,\n })\n .appState('reports.reserve-report', {\n title: 'Reserve Report',\n url: '/reserve-report?time&branch&sort',\n legacyUrl: '/reports/reservereport.pl',\n permissions: 'reports',\n matchRoute: true,\n widget: false,\n })\n \n //================= Serials ===============\n \n .widget('serials', {\n title: 'Serials Home',\n permissions: 'serials',\n group: 'basic',\n description: 'Serials Home',\n\n //registerState: false,\n //matchRoute: false,\n //widget: {}\n })\n\n //================= Circ ===============\n \n .appState('circ', {\n title: 'Circulation',\n url: '/circ',\n templateUrl: 'circ.html',\n controller: 'StaffCirculationCtrl',\n permissions: {circulate: '*'},\n matchRoute: {\n path: '/bvcgi/circ/circulation-home.pl',\n },\n widget: {}\n })\n .appState('circ.checkout', {\n title: 'Check Out',\n url: '/checkout',\n template: 'Please enter barcode or patron search terms in the resident search box.',\n permissions: {circulate: '*'},\n matchRoute: false,\n widget: {\n group: 'circ',\n },\n })\n .appState('circ.checkin', {\n title: 'Check In',\n url: '/checkin/:barcode',\n params: {\n barcode: { dynamic: true, value: null }\n },\n permissions: {circulate: '*'},\n templateUrl: 'circ/checkin.html',\n controller: 'StaffCircCheckinCtrl',\n matchRoute: {\n path: '/bvcgi/circ/returns.pl',\n },\n widget: {\n group: 'circ',\n },\n })\n .appState('circ.offline-circ', {\n title: 'Offline Circulation',\n url: '/offline-circ',\n templateUrl: 'circ/offline-circ.html',\n controller: 'StaffCircOfflineCtrl',\n matchRoute: false,\n permissions: { circulate: 'offline_circ' },\n widget: {\n group: 'circ',\n },\n })\n .appState('circ.checkin-search', {\n title: 'Check In',\n url: '/checkin',\n template: 'Please enter barcode.',\n permissions: {circulate: '*'},\n matchRoute: false,\n widget: {\n addToSidebar: false,\n outerClass: 'staff-widget-outer staff-widget-outer-bounded clearfix',\n group: 'searches',\n templateUrl: '/app/static/partials/staff/tabs/checkinTab.html',\n addTitle: true,\n },\n })\n .appState('circ.notes', {\n title: 'Notes',\n url: '/notes?op¬e_id&borrowernumber',\n legacyUrl: '/circ/notes.pl',\n permissions: {circulate: '*'},\n matchRoute: true,\n })\n .appState('circ.transfer', {\n title: 'Transfer',\n url: '/transfer',\n legacyUrl: '/circ/branchtransfers.pl',\n permissions: {circulate: 'transfer'},\n matchRoute: true,\n widget: {\n group: 'circ',\n }\n })\n .appState('circ.course-reserves', {\n title: 'Course Reserves',\n url: '/course-reserves?op&submit&limit&instructor_borrowernumber&course_reserve_id&course_id&ErrorCourseReservesExist',\n legacyUrl: '/circ/courses.pl',\n permissions: {circulate: 'put_coursereserves'},\n matchRoute: true,\n widget: {\n group: 'circ',\n }\n })\n .appState('circ.ill-requests', {\n title: 'Interlibrary Loan Requests',\n url: '/ill-requests/:id?op',\n params: {\n id: null\n },\n legacyUrl: '/circ/ill.pl',\n permissions: {circulate: 'manage_ill_requests'},\n matchRoute: true,\n widget: {\n group: 'circ',\n }\n })\n .appState('circ.callslips', {\n title: 'Call Slip Requests',\n url: '/callslips?callslip_id&op&biblionumber',\n legacyUrl: '/circ/callslips.pl',\n permissions: {circulate: '*'},\n matchRoute: function(params) {\n return ('barcode' in params ? false : true);\n },\n widget: {\n group: 'reports',\n }\n })\n .appState('circ.holds-queue', {\n title: 'Holds Queue',\n url: '/holds-queue',\n templateUrl : 'circ-holds-queue.html',\n controller: 'StaffCircHoldsQueueCtrl',\n permissions: {circulate: '*'},\n matchRoute: {\n path: '/bvcgi/circ/view_holdsqueue.pl',\n },\n widget: {\n group: 'reports',\n }\n })\n .appState('circ.waiting-reserves', {\n title: 'Holds Awaiting Pickup',\n url: '/waiting-reserves',\n legacyUrl: '/circ/waitingreserves.pl',\n permissions: {circulate: '*'},\n matchRoute: true,\n widget: {\n group: 'reports',\n }\n })\n .appState('circ.cancelled-holds', {\n title: 'Cancelled Holds',\n url: '/cancelled-holds',\n legacyUrl: '/circ/cancelledholds.pl',\n permissions: {circulate: '*'},\n matchRoute: true,\n widget: {\n group: 'reports',\n }\n }) \n .appState('circ.transfers', {\n title: 'Transfers To Receive',\n url: '/transfers?itemnumber',\n legacyUrl: '/circ/transferstoreceive.pl',\n permissions: {circulate: '*'},\n matchRoute: true,\n widget: {\n group: 'reports',\n }\n })\n .appState('circ.overdues', {\n title: 'Overdues',\n url: '/overdues?patron_attr_filter_agency&pattron_attr_filter_department&patron_attr_filter_TOWNSHIP&order&showall&borname&borcat&itemtype&borflag&branch&pickupbranch&op&search',\n legacyUrl: '/circ/overdue.pl',\n permissions: {circulate: '*'},\n matchRoute: true,\n widget: {\n group: 'reports',\n description: 'WARNING! This report can be very resource intensive on systems with large numbers of overdue items',\n }\n })\n .appState('circ.callslips-search', {\n title: 'Fill Call Slip',\n url: '/callslips-search?barcode&op&biblionumber&callslip_id',\n legacyUrl: '/circ/callslips.pl',\n permissions: {circulate: '*'},\n matchRoute: function(params) {\n return ('barcode' in params ? true : false);\n },\n widget: {\n addToSidebar: false,\n outerClass: 'staff-widget-outer staff-widget-outer-bounded clearfix',\n group: 'searches',\n templateUrl: '/app/static/partials/staff/tabs/callslipFillTab.html',\n addTitle: true,\n }\n })\n\n // No widgets, just routes and matching\n\n .appState('circ.fastcat', {\n title: 'Circulation',\n url: '/fastcat?borrowernumber&duedatespec&location&barcode&branch&stickyduedate',\n legacyUrl: '/circ/fastcat.pl',\n permissions: {circulate: '*'},\n matchRoute: true,\n widget: false,\n })\n .appState('circ.fast-add', { // adds an item, but from within patron view.\n url: '/fast-add?op&format&borrowernumber&biblionumber&holdingbranch',\n legacyUrl: '/catalogue/fastadd.pl',\n matchRoute: true,\n widget: false\n })\n .appState('circ.billing', {\n title: 'Circ Billing',\n url: '/billing?from&to&order',\n legacyUrl: '/circ/billing.pl',\n permissions: {circulate: '*'},\n matchRoute: true,\n widget: false,\n })\n .appState('circ.pending-reserves', {\n title: 'Circ Pending Reserves',\n url: '/pending-reserves?order&from&to&theme',\n legacyUrl: '/circ/pendingreserves.pl',\n permissions: {circulate: '*'},\n matchRoute: true,\n widget: false,\n })\n .appState('circ.branch-printer', {\n title: 'Circ Select Branch Printer',\n url: '/branch-printer',\n legacyUrl: '/circ/selectbranchprinter.pl',\n permissions: {circulate: '*'},\n matchRoute: true,\n widget: false,\n })\n\n //================= Patrons ===============\n \n .appState('patrons', {\n title: 'Patrons',\n url: '/patrons',\n templateUrl: 'patrons.html',\n controller: 'StaffPatronsCtrl',\n permissions: {borrowers: '*'},\n matchRoute: false,\n widget: false\n })\n .appState('patrons.browse', {\n title: 'Search/Browse Patrons',\n url: '/browse',\n legacyUrl: '/members/members-home.pl',\n permissions: {borrowers: '*'},\n matchRoute: true,\n widget: {}\n })\n .appState('patrons.addnew', {\n title: 'Add New Patron',\n url: '/addnew?op&step&destination&guarantorid&categorycode&category_type©&firstname&surname&email&dateofbirth&address&address2&city&state&zipcode&country&phone¬ify',\n legacyUrl: '/members/memberentry.pl',\n params: { op: 'add' },\n permissions: { borrowers: { create: '*'} },\n matchRoute: function(params) {\n return ((params.op != 'add') ? false : true);\n },\n widget: {},\n })\n .appState('patrons.patron_reg', {\n title: 'Patron Registrations',\n url: '/patron_reg',\n templateUrl: 'patrons/patron_reg.html',\n controller: 'StaffPatronRegCtrl',\n permissions: { borrowers: 'create' },\n matchRoute: false,\n widget: {\n addToSidebar: true,\n group: 'patrons',\n displayIf: function() { return KOHA.config.patronSelfRegister; },\n }\n })\n .appState('patrons.search', {\n title: 'Search Patrons',\n url: ('/search/:member?quicksearch&startfrom&resultsperpage&theme&orderby&from_list_id&currPage'\n + '&advanced_patron_search&borrowernumber&cardnumber&categorycode&dateenrolled_after'\n + '&dateenrolled_before&dateexpiry_after&dateexpiry_before&branchcode&sort1&sort2&userid'\n + '&dateofbirth_after&dateofbirth_before&surname&firstname&address&city&zipcode&B_address'\n + '&B_city&B_zipcode&email&emailpro&phone&opacnotes&borrowernotes&debarred&gonenoaddress'\n + '&lost&limit&offset&add_to_list&list_id&list_name&type'),\n legacyUrl: '/members/member.pl',\n permissions: {borrowers: '*'},\n params: {\n member: null\n },\n matchRoute: true,\n widget: {\n outerClass: 'staff-widget-outer staff-widget-outer-bounded clearfix',\n templateUrl: '/app/static/partials/staff/tabs/patronSearchTab.html',\n addTitle: true,\n addToSidebar: false,\n }\n })\n .appState('patrons.bulk-edit', {\n title: 'Bulk Edit Patrons',\n url: '/bulk-edit?param_borrower_list&delete',\n legacyUrl: '/members/bulkedit.pl',\n permissions: {borrowers: '*'},\n matchRoute: true,\n widget: false,\n })\n // .appState('patrons.basket', {\n // basket is legacy child window.\n // title: 'Patron Borrower Basket',\n // url: '/basket?op&borrower_list&print&verbose',\n // legacyUrl: '/members/borrower-basket.pl',\n // permissions: {borrowers: '*'},\n // matchRoute: true,\n // widget: false,\n // })\n\n .appState('patron', {\n title: 'Patron',\n url: '/patron/:borrowernumber',\n templateUrl: 'patrons.html', // note we reuse patrons template.\n controller: 'StaffPatronCtrl',\n matchRoute: false\n })\n\n .appState('patron.checkout', {\n title: 'Check Out',\n url: '/checkout?duedatespec&barcode&branch&dateexpiry&stickyduedate&mute_soundon&view_all&fa',\n legacyUrl: '/circ/circulation.pl',\n permissions: {circulate: '*'},\n matchRoute: function(qp){\n var params = {...qp};\n if(params.fastadd){\n delete params.barcode;\n delete params.fastadd;\n }\n return {\n state: 'staff.patron.checkout',\n params: params\n }\n\n },\n// fixme: probably no widget here.\n widget: {\n outerClass: 'staff-widget-outer staff-widget-outer-bounded clearfix',\n templateUrl: '/app/static/partials/staff/tabs/checkoutTab.html',\n addTitle: true,\n addToSidebar: true,\n }\n })\n .appState('patron.details', {\n title: 'Details',\n url: '/details?reregistration&error&print&bnum&op&as&eslip',\n legacyUrl: '/members/moremember.pl',\n permissions: {borrowers: '*'},\n matchRoute: true,\n widget: {},\n })\n\n .appState('patron.modification-log', {\n title: 'Patron Modification Log',\n url: '/log-viewer?object&src&do_it&modules',\n legacyUrl: '/tools/viewlog.pl?do_it=1&modules=MEMBERS',\n matchRoute: false, // see tools.viewlog\n permissions: {tools: 'view_system_logs'},\n widget: {},\n })\n .appState('patron.messaging', {\n title: 'Messages',\n url: '/messaging?message_endpoint_id',\n legacyUrl: '/members/messaging.pl',\n permissions: {borrowers: '*'},\n matchRoute: true,\n widget: {},\n })\n .appState('patron.proxy-relationships', {\n title: 'Proxy Borrowing',\n url: '/proxy-relationships?redirect&proxy_borrowernumber&op',\n legacyUrl: '/members/proxy_relationships.pl',\n permissions: {borrowers: '*'},\n matchRoute: true,\n widget: {},\n })\n .appState('patron.password', {\n title: 'Patron Password',\n url: '/password',\n legacyUrl: '/members/member-password.pl?member',\n permissions: {borrowers: '*'},\n matchRoute: true,\n widget: false,\n })\n .appState('patron.permissions', {\n title: 'Patron Permissions',\n url: '/permissions?member',\n legacyUrl: '/members/member-flags.pl',\n permissions: {borrowers: '*'},\n matchRoute: true,\n widget: {},\n })\n .appState('patron.accounting', {\n title: 'Patron Accounting',\n url: '/accounting?view',\n templateUrl: 'patrons/accounting.html',\n controller: 'StaffPatronAccountingCtrl',\n permissions: {borrowers: '*'},\n matchRoute: false,\n widget: {},\n })\n .appState('patron.status', {\n title: 'Patron Status',\n url: '/status?destination&cardnumber&status&reregistration',\n legacyUrl: '/members/setstatus.pl',\n permissions: {borrowers: '*'},\n matchRoute: true,\n widget: false,\n })\n .appState('patron.edit', {\n title: 'Edit Patron',\n url: '/edit?op&step&destination&guarantorid&categorycode&category_type&borrowernumber',\n legacyUrl: '/members/memberentry.pl',\n permissions: {borrowers: '*'},\n matchRoute: true,\n widget: false,\n })\n .appState('patron.preferences', {\n title: 'Patron Preferences',\n url: '/preferences?op',\n legacyUrl: '/members/edit_user_prefs.pl',\n permissions: {borrowers: '*'},\n matchRoute: true,\n widget: {},\n })\n .appState('patron.group', {\n title: 'Patron Group',\n url: '/group',\n templateUrl: 'patrons/group.html',\n controller: 'StaffPatronGroupCtrl',\n permissions: {borrowers: '*'},\n matchRoute: false,\n widget: {},\n })\n .appState('patron.lost-items', {\n title: 'Lost Items',\n url: '/lost_items',\n permissions: {borrowers: '*'},\n templateUrl: 'patrons/lost-items.html',\n controller: 'StaffPatronLostItemsCtrl',\n matchRoute: false,\n widget: {},\n })\n .appState('patron.reading-record', {\n title: 'History',\n url: '/reading-record?order&cardnumber&limit',\n legacyUrl: '/members/readingrec.pl',\n permissions: {borrowers: '*'},\n matchRoute: true,\n widget: {},\n })\n .appState('patron.merge', {\n title: 'Patron Merge',\n url: '/merge',\n templateUrl: 'patrons/merge.html',\n controller: 'StaffPatronMergeCtrl',\n permissions: {borrowers: { merge: '*'}},\n matchRoute: false,\n widget: {},\n })\n .appState('patron.delete', {\n title: 'Patron Delete',\n url: '/delete',\n templateUrl: 'patrons/delete.html',\n controller: 'StaffPatronDeleteCtrl',\n permissions: {borrowers: { delete: '*'}},\n matchRoute: false,\n params: {\n member: null\n },\n widget: {},\n })\n .appState('patron.guarantor-search', {\n title: 'Patron Guarantor Search',\n url: '/guarantor-search?theme&member&orderby',\n legacyUrl: '/members/guarantor_search.pl',\n permissions: {borrowers: '*'},\n matchRoute: true,\n widget: false,\n })\n\n //================= Tools ===============\n \n .appState('tools', {\n title: 'Tools',\n url: '/tools',\n legacyUrl: '/tools/tools-home.pl',\n templateUrl: 'tools.html',\n controller: 'StaffToolsCtrl', \n permissions: {tools: '*'},\n matchRoute: true,\n widget: {\n addToSidebar: false\n }\n })\n .appState('tools.news', {\n title: 'News',\n url: '/news?op&id&ids',\n legacyUrl: '/tools/koha-news.pl',\n permissions: {tools: 'edit_news'},\n matchRoute: true,\n widget: {\n description: 'Write news for the OPAC and staff interfaces',\n }\n })\n .appState('tools.calendar', {\n title: 'Calendar',\n url: '/calendar?branchcode&op&summary_length',\n permissions: {tools: 'edit_calendar'},\n legacyUrl: '/tools/holidays.pl',\n matchRoute: true,\n widget: {\n description: 'Define days when the library is closed',\n }\n })\n .appState('tools.comments', {\n title: 'Comments',\n url: '/comments/:reviewid?op',\n params: {\n reviewid: null\n },\n legacyUrl: '/reviews/reviewswaiting.pl',\n permissions: {tools: 'moderate_comments'},\n matchRoute: true,\n widget: {\n description: 'Moderate patron comments',\n }\n })\n .appState('tools.tags', {\n title: 'Tags',\n url: '/tags?op&test&tags&approved&tag&from&to&approver&approved_by&limit_page&offset',\n legacyUrl: '/tags/review.pl',\n permissions: {tools: 'moderate_comments'},\n matchRoute: true,\n widget: {\n description: 'Moderate patron tags',\n }\n })\n .appState('tools.patron-lists', {\n title: 'Patron Lists',\n url: '/patron-lists/:list_id?op&list_name&member',\n params: {\n list_id: null\n },\n legacyUrl: '/tools/member_lists.pl',\n permissions: {borrowers: 'lists'},\n matchRoute: true,\n widget: {\n description: 'Create, delete, and edit Patron Lists',\n }\n })\n .appState('tools.record-attributes', {\n title: 'Record Attributes',\n url: '/record-attributes',\n legacyUrl: '/tools/record_attributes.pl',\n permissions: {tools: 'record_attributes'},\n matchRoute: true,\n widget: {\n description: 'Search and remove Record Attributes in bulk',\n }\n })\n .widget('tools.bim.index', {\n title: 'Batch Item Editor',\n permissions: {editcatalogue: {item_list: 'process'}},\n description: 'Batch edit or delete item records',\n\n //registerState: false,\n //matchRoute: false,\n //widget: {}\n })\n .widget('tools.mvr.index', {\n title: 'MARC Validation Rulesets',\n permissions: {editcatalogue: {mvr: 'get'}},\n description: 'View or modify MARC Validation Rulesets',\n })\n .widget('tools.ledger-importer.scripts', {\n title: 'Import/Export Factory',\n permissions: {admin: {io_factory: '*'}},\n description: 'Create import batches from spreadsheet or MARC data, and export catalog data in sheet form',\n\n //registerState: false,\n //matchRoute: false,\n //widget: {}\n })\n .appState('tools.marc-stage', {\n title: 'MARC Import-Stage',\n url: '/marc-stage',\n legacyUrl: '/tools/stage-marc-import.pl',\n permissions: {tools: 'stage_marc_import'},\n matchRoute: true,\n widget: {\n description: 'Stage MARC bibliographic records into the reservoir',\n }\n })\n .appState('tools.marc-batch-edit', {\n title: 'MARC Import-Batch Edit',\n url: '/marc-batch-edit',\n templateUrl: 'tools/marc-batch-edit.html',\n permissions: {tools: 'batch_edit_marc'},\n resolve: {\n batches: [ \"resolvedPermissions\", \"importBatchService\",function(resolvedPermissions, importBatchService) {\n return importBatchService.getList();\n }],\n },\n controller: 'StaffMarcBatchEditCtrl',\n matchRoute: false,\n widget: {\n description: 'Batch Edit staged MARC records using ruleset templates',\n }\n })\n .appState('tools.marc-manage', {\n title: 'MARC Import-Manage Stgd',\n url: '/marc-manage/:import_batch_id?op&offset',\n params: {\n import_batch_id: null\n },\n legacyUrl: '/tools/manage-marc-import.pl',\n permissions: {tools: 'manage_staged_marc'},\n matchRoute: true,\n widget: {\n description: 'Manage staged MARC records, including completing and reversing imports',\n }\n })\n .appState('tools.export', {\n title: 'Export Bibliographic/Holdings',\n url: '/export',\n legacyUrl: '/tools/export.pl',\n permissions: {tools: 'export_catalog'},\n matchRoute: true,\n widget: {\n description: 'Export bibliographic and holdings data',\n }\n })\n .appState('tools.callnumber', {\n title: 'Call Number Browse',\n url: '/callnumber',\n permissions: {catalogue: 'access'},\n templateUrl: 'tools/callnumber.html',\n controller: 'StaffCallNumberBrowseCtrl',\n matchRoute: false,\n widget: {\n description: 'Browse by call number',\n }\n })\n .appState('tools.import-patrons', {\n title: 'Import Patrons',\n url: '/import-patrons?sample',\n legacyUrl: '/tools/import_borrowers.pl',\n permissions: {tools: 'import_patrons'},\n matchRoute: true,\n widget: {\n description: 'Import patron data',\n }\n })\n .appState('tools.delete-patrons', {\n title: 'Delete Patrons',\n url: '/delete-patrons',\n legacyUrl: '/tools/cleanborrowers.pl',\n permissions: {tools: 'delete_anonymize_patrons'},\n matchRoute: true,\n widget: {\n description: 'Delete patrons who have not borrowed since a specific date',\n }\n })\n .appState('tools.upload-patron-images', {\n title: 'Upload Patron Images',\n url: '/upload-patron-images?cardnumber&borrowernumber&op',\n legacyUrl: '/tools/picture-upload.pl',\n permissions: {tools: 'batch_upload_patron_images'},\n matchRoute: true,\n widget: {\n description: 'Upload patron images in batch or one at a time',\n }\n })\n .widget('tools.mte.index', {\n title: 'Message Templates',\n permissions: {tools: {edit_notices: '*'}},\n description: 'Define and modify message templates (notifications for overdues, letters, etc)',\n })\n .widget('tools.send-message', {\n title: 'Send Messages',\n permissions: {tools: 'send_general_messages'},\n description: 'Send general-purpose messages and notifications',\n })\n .appState('tools.print-messages', {\n title: 'Print Messages',\n url: '/print-message',\n permissions: {tools: 'print_messages'},\n templateUrl: 'tools/print-messages.html',\n controller: 'StaffPrintMessagesCtrl',\n matchRoute: false,\n widget: {\n description: 'Print messages not sent by email',\n }\n })\n .appState('tools.overdue-rules', {\n title: 'Overdue Notices/Triggers',\n url: '/overdue-rules',\n legacyUrl: '/tools/overduerules.pl',\n permissions: {tools: 'edit_notice_status_triggers'},\n matchRoute: true,\n widget: {\n description: 'Set notice/status triggers for overdue items',\n }\n })\n .appState('tools.log-viewer', {\n title: 'Log Viewer',\n url: '/log-viewer?object&src&do_it&modules&biblionumber&borrowernumber',\n legacyUrl: '/tools/viewlog.pl',\n permissions: {tools: 'view_system_logs'},\n matchRoute: function(qp) {\n var params = {};\n if ('src' in qp) params.src = qp.src;\n if ('do_it' in qp) params.do_it = qp.do_it;\n if (qp.modules == 'CATALOGUING') {\n if ('object' in qp) params.biblionumber = qp.object;\n return {\n state: 'staff.bib.log',\n params: params\n };\n } else if(qp.modules == 'MEMBERS'){\n if ('object' in qp) params.borrowernumber = qp.object;\n return {\n state: 'staff.patron.modification-log',\n params: params\n };\n } else {\n if ('modules' in qp) params.modules = qp.modules;\n if ('object' in qp) params.object = qp.object;\n return {\n state: 'staff.tools.log-viewer',\n params: params\n };\n }\n },\n widget: {\n description: 'Browse the system logs',\n }\n })\n .appState('tools.event-log', {\n title: 'Event Log',\n url: '/event-log',\n templateUrl: 'tools/event-log.html',\n permissions: {tools: 'view_system_logs'},\n controller: 'StaffEventLogCtrl',\n resolve: {\n fieldMap: [\"kwApi\", function(kwApi) {\n return kwApi.ActionLog.getFieldMap().$promise;\n }]\n },\n matchRoute: false,\n widget: {\n description: 'View and manage the event log',\n }\n })\n .appState('tools.dlso-log-viewer', {\n title: 'DLSO Log Viewer',\n url: '/dlso-log-viewer',\n modal: true,\n controller: [\"awLogService\", function(awLogService) {\n awLogService.startModal();\n }],\n permissions: {tools: 'dlso'},\n matchRoute: false,\n widget: {\n description: 'View DLSO ingest logs',\n displayIf: function() { return KOHA.config.geospatialSearch; },\n }\n })\n .appState('tools.dlso-by-uuid', {\n title: 'UUID Lookup',\n url: '/dlso-by-uuid',\n modal: true,\n controller: function() {\n var UUID = prompt(\"Please Enter GUIDE/UUID : \", \"\");\n if (UUID == null)\n return;\n\n var patt = new RegExp(\"(GUIDE://[0-9]{4}/)?([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$\");\n if (patt.test(UUID)) {\n var scrubbedUUID = patt.exec(UUID)[2];\n window.open(\n \"../../app/static/fullviewer.html?uuid=\"+scrubbedUUID ,\n \"width=1200,height=600,scrollbars=no,toolbar=no,screenx=0,screeny=0,location=no,titlebar=no,directories=no,status=no,menubar=no\");\n } else {\n alert(\"UUID/GUIDE must be in correct GUIDE or UUID format: e.g. GUIDE://1111/11111111-1111-1111-1111-111111111111 or 11111111-1111-1111-1111-111111111111 \");\n }\n },\n permissions: {tools: 'dlso'},\n matchRoute: false,\n widget: {\n description: 'View DLSO by UUID',\n }\n })\n .appState('tools.inventory', {\n title: 'Inventory/Stocktaking',\n url: '/inventory',\n legacyUrl: '/tools/inventory.pl',\n permissions: {tools: 'inventory'},\n matchRoute: true,\n widget: {\n description: 'Perform inventory (stocktaking) of your catalog',\n }\n })\n .appState('tools.fine-threshold-forgiveness', {\n title: 'Fine Threshold Forgiveness',\n url: '/fine-threshold-forgiveness?op&id&branchcode&branch',\n legacyUrl: '/tools/fine-threshold-forgiveness.pl',\n permissions: {tools: 'fine_threshold_forgive'},\n matchRoute: true,\n widget: {\n description: 'Select fine threshold rules to run them',\n }\n })\n .appState('tools.forgiven-fines', {\n title: 'Forgiven Fines',\n url: '/forgiven-fines?op&file_id',\n legacyUrl: '/tools/forgiven-fines.pl',\n permissions: {tools: 'fine_threshold_forgive'},\n matchRoute: true,\n widget: {\n description: 'Download and manage past forgiven fines',\n }\n })\n .widget('tools.trace.index', {\n title: 'System Tracing',\n permissions: {tools: {trace: '*'}},\n description: 'Trace performance and exception conditions',\n })\n .appState('tools.substitute-user', {\n title: 'Substitute User',\n url: '/substitute-user?mode',\n templateUrl: 'tools/substitute-user.html',\n permissions: {substitute_user: '*'},\n resolve: {\n roles: [\"kwApi\", function(kwApi) {\n return kwApi.Patron.getRoleList().$promise;\n }],\n branches: [\"kwApi\", function(kwApi) {\n return kwApi.Branch.getList().$promise;\n }],\n vendors: [\"kwApi\", function(kwApi) {\n return kwApi.Vendor.getList().$promise;\n }],\n mode: [\"$stateParams\", function($stateParams) {\n return $stateParams.mode;\n }],\n },\n controller: 'StaffSubstituteUserCtrl',\n matchRoute: false,\n widget: {\n description: 'Open a new tab as another user',\n }\n })\n // No widgets, just state\n .appState('tools.patron-lists-bulkedit-confirm', {\n title: 'Patron Lists Bulk Edit Confirm',\n url: '/patron-lists-bulkedit-confirm',\n legacyUrl: '/tools/member_lists_bulkedit_confirm.pl',\n permissions: {borrowers: 'lists'},\n matchRoute: true,\n widget: false,\n })\n .appState('tools.patron-lists-bulkedit', {\n title: 'Patron Lists Bulk Edit',\n url: '/patron-lists-bulkedit?list_id',\n legacyUrl: '/tools/member_lists_bulkedit.pl',\n permissions: {borrowers: 'lists'},\n matchRoute: true,\n widget: false,\n })\n \n \n //================= Labels ===============\n //Now in its own state though we inject the widgets into the tools controller\n \n .appState('labels', {\n title: 'Tools',\n url: '/labels',\n templateUrl: 'tools.html',\n controller: 'StaffToolsCtrl', \n permissions: {tools: 'label_creator'},\n matchRoute: false,\n })\n .appState('labels.creator', {\n title: 'Layouts',\n url: '/creator?op&layout_id&batch_id',\n legacyUrl: '/labels/label-home.pl',\n permissions: {tools: 'label_creator'},\n matchRoute: true,\n widget: {\n description: 'Create printable labels and barcodes from catalog data',\n }\n })\n .appState('labels.template', {\n title: 'Templates',\n url: '/template/:tmpl_id?op&tmpl_code&width&height&topmargin&leftmargin&columns&rows&colgap&rowgap&batch_id',\n params: {\n tmpl_id: null\n },\n legacyUrl: '/labels/label-templates.pl',\n permissions: {tools: 'label_creator'},\n matchRoute: true,\n widget: {},\n })\n .appState('labels.profile', {\n title: 'Printer Profiles',\n url: '/profile/:prof_id?op&batch_id',\n params: {\n prof_id: null\n },\n legacyUrl: '/labels/label-profiles.pl',\n permissions: {tools: 'label_creator'},\n matchRoute: true,\n widget: {},\n })\n .appState('labels.batch-manager', {\n title: 'Manage Label Batches',\n url: '/batch-manager?type&debug&op&labelid&batch_id&cardid&borrowernumber&itemnumber',\n legacyUrl: '/labels/label-manager.pl',\n permissions: {tools: 'label_creator'},\n matchRoute: true,\n widget: {},\n })\n .appState('labels.spine-create', {\n title: 'Quick Spine Creator',\n url: '/spine-create?item',\n legacyUrl: '/labels/spinelabel-home.pl',\n permissions: {tools: 'label_creator'},\n matchRoute: true,\n widget: {\n description: 'Enter a barcode to generate a printable spine label',\n }\n })\n .appState('labels.spine-print', {\n title: 'Print Spine Label',\n url: '/labels-spine-print?barcode',\n legacyUrl: '/labels/spinelabel-print.pl',\n permissions: {tools: 'label_creator'},\n matchRoute: true,\n widget: false,\n })\n .appState('labels.print-pdf', {\n title: 'Labels Print PDF',\n url: '/labels-print-pdf/?batch_id&type',\n legacyUrl: '/labels/label-print-pdf.pl',\n permissions: {tools: 'label_creator'},\n matchRoute: true,\n widget: false,\n })\n .appState('labels.print-csv', {\n title: 'Labels Print CSV',\n url: '/labels-print-csv/?batch_id&type',\n legacyUrl: '/labels/label-print-csv.pl',\n permissions: {tools: 'label_creator'},\n matchRoute: true,\n widget: false,\n }) \n .appState('labels.layout-edit', {\n title: 'Label Edit Layout',\n url: '/layout/:layout_id/edit', \n legacyUrl: '/labels/label-edit-layout.pl',\n params: {\n layout_id: null\n },\n permissions: {tools: 'label_creator'},\n matchRoute: true,\n widget: false,\n })\n .appState('labels.item-search', {\n title: 'Label Item Search',\n url: '/item-search?batch_id&resultsperpage&i_itemnumber&datefrom&startfrom&dateto&ccl_query&op',\n legacyUrl: '/labels/label-item-search.pl',\n permissions: {tools: 'label_creator'},\n matchRoute: true,\n widget: false,\n })\n .appState('labels.profile-edit', {\n title: 'Label Edit Printer Profile',\n url: '/profile/:prof_id/edit?op&offset_horz&offset_vert&creep_horz&creep_vert&unit',\n legacyUrl: '/labels/label-edit-profile.pl',\n permissions: {tools: 'label_creator'},\n matchRoute: true,\n widget: false,\n })\n .appState('labels.profile-create', {\n title: 'Label Create Printer Profile',\n url: '/profile/:prof_id/create?op&printername&paper_bin&tmpl_id&offset_horz&offset_vert&creep_horz&creep_vert&unit',\n legacyUrl: '/labels/label-create-profile.pl',\n params: {\n prof_id: null\n },\n permissions: {tools: 'label_creator'},\n matchRoute: true,\n widget: false,\n })\n .appState('labels.template-edit', {\n title: 'Label Edit Template',\n url: '/template/:tmpl_id/edit?width&height&topmargin&leftmargin&columns&rows&colgap&rowgap&prof_id',\n legacyUrl: '/labels/label-edit-template.pl',\n permissions: {tools: 'label_creator'},\n matchRoute: true,\n widget: false,\n })\n .appState('labels.template-save', {\n title: 'Label Create Template',\n url: '/template/create?op&batch_id&fontsize&fonts&units&rowgap&colgap&rows&cols&leftmargin&topmargin&label_width&label_height&page_width&page_height&tmpl_desc&tmpl_code',\n legacyUrl: '/labels/label-create-template.pl',\n permissions: {tools: 'label_creator'},\n matchRoute: true,\n widget: false,\n })\n .appState('labels.test-split', {\n title: 'Label Test Split',\n url: '/test-split?r&t',\n legacyUrl: '/labels/label-testsplit.pl',\n permissions: {tools: 'label_creator'},\n matchRoute: true,\n widget: false,\n })\n\n\n //================= Bib Details ===============\n\n .appState('bib', {\n title: 'Bib',\n url: '/bib/:biblionumber',\n matchRoute: false,\n templateUrl: 'bib.html',\n controller: 'StaffBibCtrl',\n params: {\n biblionumber: null,\n },\n })\n .appState('bib.details', {\n title: 'Detail',\n url: '/details?q&item&type&bi&time',\n templateUrl: 'bib-detail-staff.html',\n legacyUrl: '/catalogue/detail.pl',\n matchRoute: true,\n // matchRoute: false,\n widget: {\n addToDock: false\n },\n controller: 'StaffBibDetailCtrl'\n })\n .appState('bib.marc', {\n title: 'MARC',\n url: '/marc?frameworkcode&popup&subscriptionid',\n template: '

MARC data view of {{bib.title}}

',\n controller: [\"$scope\", \"$stateParams\", \"bibService\", \"$http\", function($scope, $stateParams, bibService, $http) {\n bibService.get($stateParams.biblionumber).then(bib => {\n $scope.bib = bib;\n $http.get('/api/work/' + $scope.bib.id + '/exports/text').then(r => {\n $scope.marc = r.data;\n $scope.type = 'MARC';\n });\n });\n }],\n matchRoute: false,\n widget: {\n addToDock: false\n }\n })\n .appState('bib.isbd', {\n title: 'ISBD',\n url: '/isbd',\n legacyUrl: '/catalogue/ISBDdetail.pl',\n matchRoute: true,\n widget: {\n addToDock: false\n }\n })\n .appState('bib.migrate', {\n title: 'Migrate Item',\n url: '/item-migrate',\n permissions: {editcatalogue: {item: '*'}},\n templateUrl: 'item-migrate.html',\n controller: 'StaffItemMigrateCtrl',\n matchRoute: false,\n widget: false\n })\n .appState('bib.items', {\n title: 'Edit Items',\n url: '/items?edit', // optional (consumed) param indicates that modal (or sub-table editor) is open. May repeat.\n permissions: {editcatalogue: {item: '*'}},\n templateUrl: 'items-detail.html',\n controller: 'StaffItemsCtrl',\n // resolve: {\n // holdings: function($stateParams, resolvedPermissions, bibService, loading) {\n // loading.add('bib.items.resolve');\n // return bibService.holdings($stateParams.biblionumber, { cache: false });\n // }\n // },\n matchRoute: false,\n reloadOnSearch: false,\n params: {\n edit: {\n value: undefined,\n array: true\n }\n },\n widget: {\n addToDock: false,\n }\n })\n .appState('bib.items_status', {\n title: 'Item Statuses',\n url: '/items/circstatus?itemid',\n permissions: {catalogue : {access: '*'}}, // also FIXME.\n templateUrl: 'items-circ-status.html',\n controller: 'StaffItemsCircStatusCtrl',\n matchRoute: false,\n widget: {\n addToDock: false\n }\n })\n // .appState('bib.item', {\n // url: '/item/:itemnumber?type&bi&subject&title',\n // legacyUrl: '/catalogue/moredetail.pl',\n // matchRoute: function(qp) {\n // return ('itemnumber' in qp ? true : false);\n // },\n // widget: false\n // })\n .appState('bib.items_status_legacy', {\n title: 'Item Statuses',\n url: '/items/circstatus-legacy',\n permissions: {catalogue : {access: '*'}}, // also FIXME.\n legacyUrl: '/catalogue/moredetail.pl',\n matchRoute: true,\n widget: false\n })\n .appState('bib.holds', {\n title: 'Holds',\n url: '/holds?multi_hold&biblionumbers&findborrower&cardnumber&restrict&page&as',\n legacyUrl: '/reserve/request.pl',\n matchRoute: true,\n widget: {\n addToDock: false\n }\n })\n .appState('bib.history', {\n title: 'Checkout History',\n url: '/history?itemnumber',\n legacyUrl: '/catalogue/issuehistory.pl',\n matchRoute: true,\n widget: {\n addToDock: false\n }\n })\n .appState('bib.item-circ-history', {\n title: 'Item Checkout History',\n url: '/items/:itm/circ-history',\n legacyUrl: '/circ/bookcount.pl',\n permissions: {circulate: '*'},\n matchRoute: true,\n widget: false,\n })\n .appState('bib.log', {\n title: 'Modification Log',\n url: '/log?src&do_it&modules',\n legacyUrl: '/tools/viewlog.pl?modules=CATALOGUING&do_it=1',\n matchRoute: false, // see tools.log-viewer\n widget: {\n addToDock: false\n }\n })\n .appState('bib.merge', {\n url: '/merge?confirm_bib_merge&bib_merge_id&cancel_bib_merge&bib_save_id_lookup&bib_save_id&op',\n legacyUrl: '/catalogue/mergebib.pl',\n matchRoute: true,\n widget: false\n })\n .appState('bib.export', {\n url: '/export?op&format',\n legacyUrl: '/catalogue/export.pl',\n matchRoute: true, // FIXME -- just for download.\n widget: false\n })\n .appState('bib.showmarc', {\n url: '/showmarc?importid&viewas',\n legacyUrl: '/catalogue/showmarc.pl',\n matchRoute: true,\n widget: false\n })\n .appState('bib.detail-print', {\n url: '/detail-print',\n legacyUrl: '/catalogue/detailprint.pl',\n matchRoute: true,\n widget: false\n })\n .appState('bib.labeled-marc', {\n url: '/labeled-marc?frameworkcode&popup',\n legacyUrl: '/catalogue/labeledMARCdetail.pl',\n matchRoute: true,\n widget: false\n })\n .appState('bib.holds-modify', {\n title: 'Modify Holds',\n url: '/holds-modify?CancelBorrowerNumber&CancelBiblioNumber&CancelItemnumber',\n legacyUrl: '/reserve/modrequest.pl',\n matchRoute: true,\n widget: false\n })\n .appState('bib.holds-place', {\n title: 'Place Holds',\n url: '/holds-place',\n legacyUrl: '/reserve/placerequest.pl',\n matchRoute: true,\n widget: false\n })\n\n // Place non-biblio-specific catalog/* under 'cat' state with same controller and template\n \n .appState('cat', {\n url: '/cat',\n matchRoute: false,\n templateUrl: 'bib.html',\n controller: 'StaffBibCtrl',\n })\n .appState('cat.facets', {\n url: '/facets?facet&solr_fq&offset&solr_query',\n legacyUrl: '/catalogue/facets.pl',\n matchRoute: true,\n widget: false\n })\n\n// TODO: Replace with /bib/:id/item/{:id}/edit\n\n // .appState('cat.additem', {\n // url: '/additem?biblionumber&itemnumber&error&items&op&mfhd_id&frameworkcode&new_mfhd_id',\n // legacyUrl: '/cataloguing/additem.pl',\n // permissions: {editcatalogue: '*'},\n // matchRoute: true,\n // widget: false\n // })\n\n // ============ Auth ===========\n \n .appState('authorities', {\n url: '/authorities',\n title: 'Authorities',\n matchRoute: false,\n templateUrl: 'authorities.html',\n controller: 'StaffAuthoritiesCtrl',\n widget: {\n addToSidebar: false\n }\n })\n .appState('authorities.search', {\n title: 'Search Authorities',\n url: '/search/:q?op&authtypecode&start&authid&index&idx&orderby&value_mainstr&type&operator',\n legacyUrl: '/authorities/authorities-home.pl',\n matchRoute: true, // FIXME - partition this route\n permissions: {editcatalogue: '*'},\n widget: {\n outerClass: 'staff-widget-outer staff-widget-outer-bounded clearfix',\n templateUrl: '/app/static/partials/staff/tabs/authSearchTab.html',\n addToSidebar: false,\n addTitle: true\n }\n })\n .appState('authorities.detail', {\n title: 'Authorities Detail',\n url: '/:authid/detail?rcn',\n legacyUrl: '/authorities/detail.pl',\n permissions: {editcatalogue: '*'},\n matchRoute: true,\n widget: false,\n })\n .appState('authorities.show-marc', {\n url: '/show-marc?id&importid&viewas',\n legacyUrl: '/catalogue/auth-showmarc.pl',\n matchRoute: true,\n widget: false\n })\n .appState('authorities.cart', {\n url: '/cart?op&authIdparent&authid&authids',\n legacyUrl: '/authorities/authcart.pl',\n matchRoute: true,\n widget: false\n })\n\n // ============== Other top-level states ==========\n\n .appState('lists', {\n title: 'Lists',\n url: '/lists?display&viewshelf&shelfoff&shelves&op&shelfnumber&itemoff&sortfield',\n legacyUrl: '/virtualshelves/shelves.pl',\n matchRoute: true,\n widget: {}\n })\n .widget('marced.home', {\n title: 'Cataloging Editor',\n permissions: {editcatalogue: '*'},\n //registerState: false,\n //matchRoute: false,\n //widget: {}\n })\n .appState('about', {\n title: 'About',\n url: '/about',\n matchRoute: false,\n modal: true,\n widget: {\n template: '',\n titleClass: 'staff-widget-page-title',\n },\n })\n .appState('acquisitions', {\n title: 'Acquisitions',\n url: '/acquisitions',\n legacyUrl: '/acqui/getit.pl',\n matchRoute: true,\n permissions: {acquisition: '*'},\n controller: [\"$scope\", function($scope) {\n $scope.masthead.docked = 'auto';\n }],\n widget: {\n titleClass: 'staff-widget-page-title',\n displayIf: function() { return KOHA.config.GetItAcquisitions; },\n }\n })\n .widget('catalog-search', {\n title: 'Search Catalog',\n outerClass: 'staff-widget-outer staff-widget-outer-bounded clearfix',\n templateUrl: '/app/static/partials/staff/tabs/catSearchTab.html',\n addTitle: true,\n addToSidebar: false,\n })\n .widget('barcode-search', {\n title: 'Barcode Search',\n outerClass: 'staff-widget-outer staff-widget-outer-bounded clearfix',\n templateUrl: '/app/static/partials/staff/tabs/barcodeSearch.html',\n addTitle: true,\n addToSIdebar: false,\n })\n .widget('search-title', {\n title: 'Search',\n permissions: {circulate: '*'},\n titleClass: 'staff-widget-page-title',\n template: ''\n })\n ;\n\n}]);\n\n\n})();\n","/* global pdfMake */\n(function () {\n'use strict';\n\nangular.module('kohapac').config([\"staffLegacyAppProvider\", \"$stateProvider\", \"$injector\",\n function(staffLegacyAppProvider, $stateProvider, $injector) {\n\n // See staff-apps.js for app declarations and menus\n\n $stateProvider.state('staff', {\n url: '/app/staff',\n templateUrl: '/app/static/partials/staff/index.html',\n controller: 'StaffCtrl',\n data: {\n pageSubTitle: 'Staff Home',\n },\n resolve: {\n widgetIdx: function() { return staffLegacyAppProvider.widgets; },\n widgetList: function() { return staffLegacyAppProvider.widgetDock; },\n resolvedPermissions: [\"userService\", function(userService) {\n return userService.whenAuthenticatedUserCan({catalogue: {access: '*'}}, null, {redirectOnFail: '/'});\n }],\n },\n }).state('staff.marced', {\n url: '/marced',\n abstract: true,\n resolve: {\n loadMarced: [\"$ocLazyLoad\", \"$injector\", function($ocLazyLoad, $injector){\n\n var scripts = [\n '/marced/css/select2.css',\n '/marced/css/angular-ui-select.min.css',\n '/marced/lib/leaflet/leaflet.css',\n '/marced/css/app.css',\n '/marced/lib/pazpar_client.js',\n '/marced/js/services.js',\n '/marced/js/directives.js',\n '/marced/js/filters.js',\n '/marced/js/controllers.js',\n '/marced/lib/leaflet/leaflet.js',\n '/marced/js/geobuilder.js',\n '/marced/js/app.js'\n ];\n\n return $ocLazyLoad.load(\n { serie:true, files:scripts.map(function(s){ return s; }) }\n ).then(function(){\n return $injector.get('kmConfigSvc').loaded;\n });\n }]\n },\n templateUrl: '/marced/index.html',\n data: {\n pageSubTitle: 'Cataloging Editor',\n module: 'marced'\n },\n controller: [\"$scope\", function($scope) {\n\n $scope.masthead.docked = 'auto';\n $scope.showSidebar = false;\n $scope.globalfooter.visible = false;\n $scope.$on(\"$destroy\", function(){\n $scope.globalfooter.visible = true; //FIXME: should be managed elsewhere.\n });\n\n }]\n })\n //// FIXME: Use FutureStates.\n .state('staff.marced.home', {\n url: '?clone&z3950',\n views: {\n 'main@staff.marced': {\n templateUrl: '/marced/partials/home.html',\n controller: 'kmNavbarCtrl'\n },\n marcedNav: {\n templateUrl: '/marced/partials/kmNavbar.html',\n controller: 'kmNavbarCtrl'\n }\n }\n })\n .state('staff.marced.editor', {\n // parent: 'staff.marced', // blah. for ref consisitency, use fully qualified name.\n url: '/edit/:record',\n views: {\n 'main': {\n templateUrl: '/marced/partials/editor-container.html'\n },\n marcedNav: {\n templateUrl: '/marced/partials/kmNavbar.html',\n controller: 'kmNavbarCtrl'\n },\n 'editor@staff.marced.editor': {\n templateUrl: '/marced/partials/editor.html',\n controller: 'EditorCtrl as editor'\n },\n 'metadata@staff.marced.editor': {\n templateUrl: '/marced/partials/metadata.html',\n controller: 'MetadataCtrl as recordMeta'\n },\n 'tabs@staff.marced.editor': {\n templateUrl: '/marced/partials/tabs.html',\n controller: 'TabsCtrl'\n },\n 'help': {\n templateUrl: '/marced/partials/help.html',\n controller: 'HelpCtrl'\n }\n },\n data: {\n editor: true\n },\n params: {\n reload: false\n },\n resolve: {\n recordid: [\"$stateParams\", function($stateParams){ return $stateParams.record; }],\n split: function(){ return false; }\n }\n })\n .state('staff.marced.editor.splitview', {\n url: '/splitview/:refrecord',\n views: {\n 'editor_ref@staff.marced.editor': {\n templateUrl: '/marced/partials/editor.html',\n controller: 'EditorCtrl as editor'\n },\n 'metadata_ref@staff.marced.editor': {\n templateUrl: '/marced/partials/metadata.html',\n controller: 'MetadataCtrl as recordMeta'\n }\n },\n resolve: {\n recordid: [\"$stateParams\", function($stateParams){ return $stateParams.refrecord; }],\n split: function(){ return true; }\n },\n data: {\n editor: true\n }\n })\n\n .state('staff.admin.appconfig', {\n url: '/config',\n templateUrl: '/app/static/partials/config-modal.html', // Not a modal anymore.\n controller: 'AppConfigCtrl',\n data: {\n requiresAuth: true,\n pageSubTitle: 'Discovery Layer Editor',\n },\n resolve: {\n resolvedPermissions: [\"userService\", function(userService) {\n return userService.whenAuthenticatedUserCan('superlibrarian', null, {redirectOnFail: '/'});\n }],\n }\n })\n .state('config', {\n url: '/app/config',\n redirectTo: 'staff.admin.appconfig',\n })\n\n .state('staff.admin.vendors', {\n url: '/vendors',\n templateUrl: '/app/static/partials/staff/vendors/index.html',\n controller: 'StaffVendorsCtrl',\n resolve: {\n vendors: [\"kwApi\", function(kwApi) {\n return kwApi.Vendor.getList().$promise;\n }],\n },\n })\n\n .state('staff.admin.vendor', {\n url: '/vendor/:vendor_id',\n templateUrl: '/app/static/partials/staff/vendors/detail.html',\n controller: 'StaffVendorsDetailCtrl',\n resolve: {\n vendor: [\"$stateParams\", \"kwApi\", function($stateParams, kwApi) {\n //console.log($stateParams.vendor_id);\n return kwApi.Vendor.get({id: $stateParams.vendor_id}).$promise;\n }]\n },\n })\n .state('staff.admin.vendor-ledgers', {\n url: '/vendor/:vendor_id/ledgers',\n templateUrl: '/app/static/partials/staff/vendors/ledgers.html',\n controller: 'StaffVendorsLedgersCtrl',\n resolve: {\n vendor: [\"$stateParams\", \"kwApi\", function($stateParams, kwApi) {\n //console.log($stateParams.vendor_id);\n return kwApi.Vendor.get({id: $stateParams.vendor_id}).$promise;\n }],\n ledgers: [\"$stateParams\", \"kwApi\", function($stateParams, kwApi) {\n return kwApi.Vendor.getLedgers({id: $stateParams.vendor_id}).$promise;\n }],\n ledgerPriceModels: [\"$stateParams\", \"kwApi\", function($stateParams, kwApi) {\n return kwApi.Ledger.getPriceModelConfig({id: $stateParams.ledger_id}).$promise;\n }],\n },\n })\n .state('staff.admin.vendor-ledger', {\n url: '/vendor/:vendor_id/ledger/:ledger_id',\n templateUrl: '/app/static/partials/staff/vendors/ledger-detail.html',\n controller: 'StaffVendorsLedgerDetailCtrl',\n resolve: {\n vendor: [\"$stateParams\", \"kwApi\", function($stateParams, kwApi) {\n //console.log($stateParams.vendor_id);\n return kwApi.Vendor.get({id: $stateParams.vendor_id}).$promise;\n }],\n ledger: [\"$stateParams\", \"kwApi\", function($stateParams, kwApi) {\n return kwApi.Ledger.get({id: $stateParams.ledger_id}).$promise;\n }],\n ledgerPriceModels: [\"$stateParams\", \"kwApi\", function($stateParams, kwApi) {\n return kwApi.Ledger.getPriceModelConfig({id: $stateParams.ledger_id}).$promise;\n }],\n },\n })\n\n // Circ policies\n .state('staff.admin.circ', {\n url: '/circ',\n templateUrl: '/app/static/partials/staff/admin/circ.html',\n controller: 'StaffAdminCircBaseCtrl',\n })\n\n .state('staff.admin.circ.config', {\n url: '/config',\n templateUrl: '/app/static/partials/staff/admin/circ/config.html',\n controller: 'StaffAdminCircConfigCtrl',\n resolve: {\n circScopes: [\"kwApi\", function(kwApi) {\n return kwApi.CircControl.getScopes().$promise;\n }],\n circControl: [\"kwApi\", function(kwApi) {\n return kwApi.CircControl.query().$promise;\n }],\n },\n })\n .state('staff.admin.circ.policies', {\n url: '/policies',\n templateUrl: '/app/static/partials/staff/admin/circ/policies.html',\n controller: 'StaffAdminCircPoliciesCtrl',\n })\n \n .state('staff.admin.circ.policy', {\n url: '/policy/:id',\n templateUrl: '/app/static/partials/staff/admin/circ/policy.html',\n controller: 'StaffAdminCircPolicyCtrl',\n resolve: {\n policy: [\"kwApi\", \"$stateParams\", function(kwApi, $stateParams) {\n if ($stateParams.id > 0) {\n return kwApi.CircPolicy.get({id: $stateParams.id}).$promise;\n }\n else {\n return {args: '{}'};\n }\n }],\n },\n })\n .state('staff.admin.circ.rules', {\n url: '/rules',\n templateUrl: '/app/static/partials/staff/admin/circ/rules.html',\n controller: 'StaffAdminCircRulesCtrl',\n })\n .state('staff.admin.circ.recall-rules', {\n url: '/recall-rules',\n templateUrl: '/app/static/partials/staff/admin/circ/recall-rules.html',\n controller: 'StaffAdminCircRecallRulesCtrl',\n })\n \n .state('staff.admin.circ.recall-rule', {\n url: '/recall-rule/:id',\n templateUrl: '/app/static/partials/staff/admin/circ/recall-rule.html',\n controller: 'StaffAdminCircRecallRuleCtrl',\n })\n \n .state('staff.admin.circ.termsets', {\n url: '/termsets',\n templateUrl: '/app/static/partials/staff/admin/circ/termsets.html',\n controller: 'StaffAdminCircTermsetsCtrl',\n })\n \n .state('staff.admin.circ.termset', {\n url: '/termset/:id',\n templateUrl: '/app/static/partials/staff/admin/circ/termset.html',\n controller: 'StaffAdminCircTermsetCtrl',\n resolve: {\n termset: [\"kwApi\", \"$stateParams\", function(kwApi, $stateParams) {\n if ($stateParams.id > 0) {\n return kwApi.CircTermset.get({id: $stateParams.id}).$promise;\n }\n else {\n return {args: '{}'};\n }\n }],\n termDates: [\"kwApi\", \"$stateParams\", function(kwApi, $stateParams) {\n if ($stateParams.id > 0) {\n return kwApi.CircTermset.getDates({id: $stateParams.id}).$promise;\n }\n else {\n return {args: '[]'};\n }\n }],\n },\n })\n \n .state('staff.admin.ncip', {\n url: '/ncip',\n templateUrl: '/app/static/partials/staff/admin/ncip.html',\n controller: 'StaffAdminNcipBaseCtrl',\n })\n .state('staff.admin.ncip.agencies', {\n url: '/agencies',\n templateUrl: '/app/static/partials/staff/admin/ncip/agencies.html',\n controller: 'StaffAdminNcipAgenciesCtrl',\n })\n .state('staff.admin.ncip.agency', {\n url: '/agency/:id',\n templateUrl: '/app/static/partials/staff/admin/ncip/agency.html',\n controller: 'StaffAdminNcipAgencyCtrl',\n resolve: {\n ncipAgency: [ \"kwApi\", \"$stateParams\", function(kwApi, $stateParams) {\n if ($stateParams.id > 0) {\n return kwApi.NcipAgency.get({id: $stateParams.id}).$promise;\n }\n else {\n return {args: '{}'};\n }\n }],\n },\n })\n \n // Serials\n .state('staff.serials', {\n url: '/serials',\n templateUrl: '/app/static/partials/staff/serials.html',\n controller: 'StaffSerialsCtrl',\n })\n\n .state('staff.serials.index', {\n url: '/serials?title&issn',\n templateUrl: '/app/static/partials/staff/serials/index.html',\n controller: 'StaffSerialsIndexCtrl',\n resolve: {\n periodicals: [\"$stateParams\", \"kwApi\", function($stateParams, kwApi) {\n return kwApi.Periodical.search({title: $stateParams.title, issn: $stateParams.issn, start: 0, count: 20}).$promise;\n }]\n }\n })\n\n .state('staff.serials.periodical', {\n url: '/publication/{id:any}?bib_id',\n templateUrl: '/app/static/partials/staff/serials/periodical.html',\n controller: 'StaffSerialsPeriodicalCtrl',\n resolve: {\n periodical: [\"$stateParams\", \"kwApi\", \"$q\", function($stateParams, kwApi, $q) {\n if ($stateParams.id) {\n return kwApi.Periodical.get({id: $stateParams.id}).$promise;\n }\n else {\n return $q.when({});\n }\n }],\n subscriptions: [\"$stateParams\", \"kwApi\", \"$q\", function($stateParams, kwApi, $q) {\n if ($stateParams.id) {\n return kwApi.Periodical.getSubscriptions({id: $stateParams.id}).$promise;\n }\n else {\n return $q.when([]);\n }\n }],\n serialEditions: [\"$stateParams\", \"kwApi\", \"$q\", function($stateParams, kwApi, $q) {\n if ($stateParams.id) {\n return kwApi.Periodical.getSerialEditions({id: $stateParams.id}).$promise;\n }\n else {\n return $q.when([]);\n }\n }],\n },\n })\n .state('staff.serials.subscription', {\n url: '/subscription/:id?parent',\n templateUrl: '/app/static/partials/staff/serials/subscription.html',\n controller: 'StaffSerialsSubscriptionCtrl',\n resolve: {\n subscription: [\"$stateParams\", \"kwApi\", \"$q\", function($stateParams, kwApi, $q) {\n if ($stateParams.id) {\n return kwApi.Subscription.get({id: $stateParams.id}).$promise;\n }\n else {\n return $q.when({});\n }\n }],\n serialInstances: [\"$stateParams\", \"kwApi\", \"$q\", function($stateParams, kwApi, $q) {\n if ($stateParams.id) {\n return kwApi.Subscription.getSerialInstances({id: $stateParams.id}).$promise;\n }\n else {\n return $q.when([]);\n }\n }],\n branches: [\"kwApi\", function(kwApi) {\n return kwApi.Branch.getList().$promise;\n }],\n },\n })\n\n .state('staff.serials.receive', {\n url: '/receive/:ids',\n templateUrl: '/app/static/partials/staff/serials/subscription-receive.html',\n controller: 'StaffSerialsSubscriptionReceiveCtrl',\n resolve: {\n subscriptions: [\"$stateParams\", \"kwApi\", \"$q\", function($stateParams, kwApi, $q) {\n var ids = $stateParams.ids.split(',');\n var promises = ids.map(function(id) {\n return kwApi.Subscription.get({id: id}).$promise;\n });\n\n return $q.all(promises);\n }],\n serialInstances: [\"$stateParams\", \"kwApi\", \"$q\", function($stateParams, kwApi, $q) {\n var ids = $stateParams.ids.split(',');\n var promises = ids.map(function(id) {\n return kwApi.Subscription.getSerialInstances({id: id, scope: 'unreceived'}).$promise;\n });\n\n return $q.all(promises);\n }],\n },\n })\n\n .state('staff.serials.edition', {\n url: '/edition/:id',\n templateUrl: '/app/static/partials/staff/serials/edition.html',\n controller: 'StaffSerialsEditionCtrl',\n resolve: {\n serialEdition: [\"$stateParams\", \"kwApi\", \"$q\", function($stateParams, kwApi, $q) {\n return kwApi.SerialEdition.get({id: $stateParams.id}).$promise;\n }],\n serialInstances: [\"$stateParams\", \"kwApi\", \"$q\", function($stateParams, kwApi, $q) {\n return kwApi.SerialEdition.getSerialInstances({id: $stateParams.id}).$promise;\n }],\n branches: [\"kwApi\", function(kwApi) {\n return kwApi.Branch.getList().$promise;\n }],\n },\n })\n\n .state('staff.serials.claims', {\n url: '/claims',\n templateUrl: '/app/static/partials/staff/serials/claims.html',\n controller: 'StaffSerialsClaimsCtrl',\n resolve: {\n branches: [\"kwApi\", function(kwApi) {\n return kwApi.Branch.getList().$promise;\n }],\n },\n })\n\n .state('staff.serials.expires', {\n url: '/renew',\n templateUrl: '/app/static/partials/staff/serials/expires.html',\n controller: 'StaffSerialsExpiresCtrl',\n resolve: {\n branches: [\"kwApi\", function(kwApi) {\n return kwApi.Branch.getList().$promise;\n }],\n },\n })\n\n .state('staff.serials.schedule-templates', {\n url: '/schedule-templates',\n templateUrl: '/app/static/partials/staff/serials/schedule-templates.html',\n controller: 'StaffSerialsScheduleTemplatesCtrl',\n resolve: {\n templates: [\"kwApi\", function(kwApi) {\n return kwApi.SerialPatternTemplate.query().$promise;\n }]\n }\n })\n .state('staff.serials.chronology-templates', {\n url: '/chronology-templates',\n templateUrl: '/app/static/partials/staff/serials/chronology-templates.html',\n controller: 'StaffSerialsChronologyTemplatesCtrl',\n resolve: {\n templates: [\"kwApi\", function(kwApi) {\n return kwApi.SerialChronologyTemplate.query().$promise;\n }]\n }\n })\n .state('staff.serials.sequence-templates', {\n url: '/sequence-templates',\n templateUrl: '/app/static/partials/staff/serials/sequence-templates.html',\n controller: 'StaffSerialsSequenceTemplatesCtrl',\n resolve: {\n templates: [\"kwApi\", function(kwApi) {\n return kwApi.SerialSequenceTemplate.query().$promise;\n }]\n }\n })\n .state('staff.serials.vendors', {\n url: '/vendors',\n templateUrl: '/app/static/partials/staff/serials/vendors.html',\n controller: 'StaffSerialsVendorsCtrl',\n resolve: {\n branches: [\"kwApi\", function(kwApi) {\n return kwApi.Branch.getList().$promise;\n }],\n }\n })\n\n // Batch Item Mutator. BIM because I'm going to get really tired of typing BatchItemMutator.\n \n .state('staff.tools.bim', {\n url: '/bim',\n templateUrl: '/app/static/partials/staff/bim.html',\n controller: 'StaffBimCtrl',\n })\n\n .state('staff.tools.bim.index', {\n url: '/index',\n templateUrl: '/app/static/partials/staff/bim/index.html',\n controller: 'StaffBimIndexCtrl',\n resolve: {\n itemLists: [\"$stateParams\", \"kwApi\", function($stateParams, kwApi) {\n return kwApi.ItemList.query({view: 'details', start: 0, sort: 'created_date', reverse: 1}).$promise;\n }],\n branches: [\"kwApi\", function(kwApi) {\n return kwApi.Branch.getList().$promise;\n }],\n }\n })\n\n .state('staff.tools.bim.view', {\n url: '/view/:id',\n templateUrl: '/app/static/partials/staff/bim/details.html',\n controller: 'StaffBimDetailsCtrl',\n resolve: {\n itemList: [\"$stateParams\", \"kwApi\", function($stateParams, kwApi) {\n return kwApi.ItemList.get({id: $stateParams.id}).$promise;\n }],\n itemListEntries: [\"$stateParams\", \"kwApi\", function($stateParams, kwApi) {\n return kwApi.ItemListEntry.query({view: 'details', item_list_id: $stateParams.id, start: 0, limit: 20, sort: 'title', reverse: 0}).$promise;\n }]\n }\n })\n\n // Message Template Editor\n \n .state('staff.tools.mte', {\n url: '/mte',\n template: '
',\n controller: 'StaffMteCtrl',\n })\n .state('staff.tools.mte.index', {\n url: '/index?mode&branch',\n templateUrl: '/app/static/partials/staff/tools/mte/index.html',\n controller: 'StaffMteIndexCtrl',\n resolve: {\n templates: [\"$stateParams\", \"kwApi\", function($stateParams, kwApi) {\n return kwApi.MessageTemplate.tree().$promise;\n }],\n branches: [\"kwApi\", function(kwApi) {\n return kwApi.Branch.getList().$promise;\n }],\n }\n })\n .state('staff.tools.mte.view', {\n url: '/view/:id',\n templateUrl: '/app/static/partials/staff/tools/mte/details.html',\n controller: 'StaffMteDetailsCtrl',\n resolve: {\n template: [\"$stateParams\", \"kwApi\", function($stateParams, kwApi) {\n return kwApi.MessageTemplate.get({id: $stateParams.id}).$promise;\n }],\n }\n })\n\n .state('staff.tools.ledger-importer', {\n url: '/ledger-importer',\n templateUrl: '/app/static/partials/staff/tools/ledger-importer.html',\n controller: 'StaffLedgerImportBaseCtrl',\n })\n\n .state('staff.tools.ledger-importer.scripts', {\n url: '/scripts',\n templateUrl: '/app/static/partials/staff/tools/ledger-importer/script-index.html',\n controller: 'StaffLedgerImportScriptsCtrl',\n resolve: {\n importScripts: [\"kwApi\", function(kwApi) {\n return kwApi.ImportScript.getList().$promise.then(function(rv) {\n rv.forEach(function(rec) {\n rec.is_approved = (rec.tainted === null || rec.tainted === '0' || rec.tainted === 0) ? true : false;\n });\n return rv;\n });\n }],\n },\n })\n .state('staff.tools.ledger-importer.scriptlets', {\n url: '/scriptlets',\n templateUrl: '/app/static/partials/staff/tools/ledger-importer/scriptlet-index.html',\n controller: 'StaffLedgerImportScriptletsCtrl',\n resolve: {\n importScriptlets: [\"kwApi\", function(kwApi) {\n return kwApi.ImportScriptlet.getList({view: 'index'}).$promise;\n }],\n },\n })\n .state('staff.tools.ledger-importer.script', {\n url: '/script/:id',\n templateUrl: '/app/static/partials/staff/tools/ledger-importer/script-update.html',\n controller: 'StaffLedgerImportScriptCtrl', \n resolve: {\n importScript: [\"kwApi\", \"$stateParams\", function(kwApi, $stateParams) {\n if ($stateParams.id === 'NEW') {\n return {script_type: 'sheet-single', _embed: {scriptlets: []}};\n }\n else {\n return kwApi.ImportScript.get({id: $stateParams.id}).$promise.then(function(rv) {\n rv.is_approved = true;\n rv._embed.scriptlets.forEach(function(s) {\n if (s.tainted !== 0 && s.tainted !== '0') {\n rv.is_approved = false;\n } \n });\n return rv;\n });\n }\n }],\n importScriptlets: [\"kwApi\", function(kwApi) {\n return kwApi.ImportScriptlet.getList().$promise;\n }],\n vendors: [\"kwApi\", function(kwApi) {\n return kwApi.Vendor.getList().$promise;\n }],\n },\n })\n .state('staff.tools.ledger-importer.script-runs', {\n url: '/script/:id/runs',\n templateUrl: '/app/static/partials/staff/tools/ledger-importer/script-runs.html',\n controller: 'StaffLedgerImportScriptRunsCtrl',\n resolve: {\n importScriptRuns: [\"kwApi\", \"$stateParams\", function(kwApi, $stateParams) {\n return kwApi.ImportScript.getRuns({id: $stateParams.id}).$promise;\n }],\n },\n })\n\n .state('staff.reports.sql', {\n url: '/sql',\n template: '
',\n controller: 'StaffReportBaseCtrl',\n })\n\n .state('staff.reports.sql.index', {\n url: '/index',\n templateUrl: '/app/static/partials/staff/reports/index.html',\n controller: 'StaffReportIndexCtrl',\n resolve: {\n reports: [\"kwApi\", function(kwApi) {\n return kwApi.Report.query().$promise;\n }],\n report_tags: [\"kwApi\", function(kwApi) {\n return kwApi.ReportTag.query().$promise;\n }],\n },\n })\n .state('staff.reports.sql.new', {\n url: '/new',\n templateUrl: '/app/static/partials/staff/reports/details.html',\n controller: 'StaffReportNewCtrl',\n })\n .state('staff.reports.sql.report', {\n url: '/report/:id',\n templateUrl: '/app/static/partials/staff/reports/details.html',\n controller: 'StaffReportDetailsCtrl',\n resolve: {\n report: [\"kwApi\", \"$stateParams\", function(kwApi,$stateParams) {\n return kwApi.Report.get({id: $stateParams.id}).$promise;\n }],\n runs: [\"kwApi\", \"$stateParams\", function(kwApi, $stateParams) {\n return kwApi.Report.getRuns({id: $stateParams.id}).$promise;\n }],\n },\n })\n .state('staff.reports.sql.runs', {\n url: '/report/:id/runs/:rid',\n templateUrl: '/app/static/partials/staff/reports/run-details.html',\n resolve: {\n report: [\"kwApi\", \"$stateParams\", function(kwApi, $stateParams) {\n return kwApi.Report.get({id: $stateParams.id}).$promise;\n }],\n run: [\"kwApi\", \"$stateParams\", function(kwApi, $stateParams) {\n return kwApi.Report.getRun({id: $stateParams.id, rid: $stateParams.rid, start: 0, count: 20}).$promise;\n }],\n fullrun: [\"kwApi\", \"$stateParams\", function(kwApi, $stateParams) {\n return kwApi.Report.getRun({id: $stateParams.id, rid: $stateParams.rid}).$promise;\n }],\n },\n controller: 'StaffReportRunDetailsCtrl',\n })\n\n // Cron jobs\n \n .state('staff.admin.cron', {\n url: '/cron',\n template: '
',\n controller: 'StaffCronCtrl',\n resolve: {\n cronScripts: [\"$stateParams\", \"kwApi\", function($stateParams, kwApi) {\n return kwApi.CronScript.query().$promise.then(function(rv) {\n return rv.filter(function(s) { return s.enabled; });\n });\n }],\n },\n\n })\n .state('staff.admin.cron.index', {\n url: '/index',\n templateUrl: '/app/static/partials/staff/admin/cron/index.html',\n controller: 'StaffCronIndexCtrl',\n resolve: {\n cronJobs: [\"$stateParams\", \"kwApi\", function($stateParams, kwApi) {\n return kwApi.CronJob.query().$promise;\n }],\n }\n })\n .state('staff.admin.cron.view', {\n url: '/view/:id',\n templateUrl: '/app/static/partials/staff/admin/cron/details.html',\n controller: 'StaffCronDetailsCtrl',\n resolve: {\n cronJob: [\"$stateParams\", \"kwApi\", \"$q\", function($stateParams, kwApi, $q) {\n if ($stateParams.id > 0) {\n return kwApi.CronJob.get({id: $stateParams.id}).$promise;\n }\n else {\n return $q.when({\n cron_script_id: 0,\n description: '', \n args: [],\n purge_after: \"30\",\n schedule: '0:0:0:1*0:0:0',\n save_stderr: true,\n save_stdout: true,\n });\n }\n }],\n cronJobRuns: [\"$stateParams\", \"kwApi\", \"$q\", function($stateParams, kwApi, $q) {\n if ($stateParams.id > 0) {\n return kwApi.CronJob.getRuns({id: $stateParams.id}).$promise;\n }\n else {\n return $q.when([]);\n }\n }],\n }\n })\n .state('staff.admin.cron.runs', {\n url: '/:id/runs/:rid',\n templateUrl: '/app/static/partials/staff/admin/cron/run-details.html',\n resolve: {\n cronJob: [\"kwApi\", \"$stateParams\", function(kwApi, $stateParams) {\n return kwApi.CronJob.get({id: $stateParams.id}).$promise;\n }],\n run: [\"kwApi\", \"$stateParams\", function(kwApi, $stateParams) {\n return kwApi.CronJob.getRun({id: $stateParams.id, rid: $stateParams.rid}).$promise;\n }],\n },\n controller: 'StaffCronRunDetailsCtrl',\n })\n\n .state('staff.tools.trace', {\n url: '/trace',\n template: '
',\n controller: 'StaffTraceCtrl',\n resolve: {\n traceOptions: [\"kwApi\", function(kwApi) {\n return kwApi.SessionApiMetadata.options().$promise;\n }],\n },\n })\n .state('staff.tools.trace.index', {\n url: '/index',\n templateUrl: '/app/static/partials/staff/tools/trace/index.html',\n controller: 'StaffTraceIndexCtrl',\n resolve: {\n traces: [\"kwApi\", \"userService\", function(kwApi, userService) { \n return userService.whenAuthenticatedUserDetails().then(function() {\n if (userService.can({tools: {trace: 'self'}}) || userService.can({tools: {trace: 'other'}})) {\n return kwApi.SessionApiMetadata.query({member_id: userService.id}).$promise;\n }\n else {\n return [];\n }\n });\n }]\n },\n })\n .state('staff.tools.trace.view', {\n url: '/view/:id',\n templateUrl: '/app/static/partials/staff/tools/trace/view.html',\n controller: 'StaffTraceViewCtrl',\n resolve: {\n trace: [\"kwApi\", \"$stateParams\", function(kwApi, $stateParams) {\n return kwApi.SessionApiMetadata.get({id: $stateParams.id}).$promise;\n }]\n },\n })\n\n .state('staff.tools.mvr', {\n url: '/mvr',\n templateUrl: '/app/static/partials/staff/tools/mvr.html',\n controller: 'StaffMvrCtrl',\n })\n\n .state('staff.tools.mvr.index', {\n url: '/index',\n templateUrl: '/app/static/partials/staff/tools/mvr/index.html',\n controller: 'StaffMvrIndexCtrl',\n resolve: {\n rulesets: [\"$stateParams\", \"kwApi\", function($stateParams, kwApi) {\n return kwApi.MarcValidationRuleset.query({view: 'brief', start: 0, limit: 20, sort: 'title', reverse: 0}).$promise;\n }],\n }\n })\n\n .state('staff.tools.mvr.view', {\n url: '/view/:id',\n templateUrl: '/app/static/partials/staff/tools/mvr/details.html',\n controller: 'StaffMvrDetailsCtrl',\n resolve: {\n ruleset: [\"$stateParams\", \"kwApi\", function($stateParams, kwApi) {\n if ($stateParams.id > 0) {\n return kwApi.MarcValidationRuleset.get({id: $stateParams.id}).$promise;\n }\n else {\n return {};\n }\n }],\n }\n })\n .state('staff.tools.send-message', {\n url: '/send-message',\n templateUrl: '/app/static/partials/staff/tools/send-message.html',\n controller: 'StaffSendMessageCtrl',\n })\n\n ;\n}]);\n\n\nangular.module('kohastaff.controllers', ['ngFileUpload'])\n\n.factory('bvStaffDlg', [\"$uibModal\", \"configService\", \"userService\", \"kwApi\", function($uibModal, configService, userService, kwApi){\n\n // globally available staff-side modal dialogs.\n return {\n patronSelect: function(){\n // returns promise resolving to patron id.\n var modalInstance = $uibModal.open({\n backdrop: true,\n templateUrl: '/app/static/partials/staff/patron-select-modal.html',\n controller: [\"$scope\", \"$uibModalInstance\", function($scope, $uibModalInstance){\n $scope.selectPatron = function(id){\n $uibModalInstance.close(id);\n };\n }],\n windowClass: \"modal patron-select-modal\",\n size: 'md',\n });\n return modalInstance.result;\n\n },\n marcImportItem: function(){\n var modalInstance = $uibModal.open({\n backdrop: true,\n templateUrl: '/app/static/partials/staff/marc-import-item-modal.html',\n controller: // 'MarcImportItemCtrl',\n [\"$scope\", \"$uibModalInstance\", function($scope, $uibModalInstance){\n $scope.item = new kwApi.Item();\n var defaults = {\n homebranch: userService.login_branch,\n holdingbranch: userService.login_branch,\n };\n configService.ItemFields.forEach(function(fieldDef){\n if(fieldDef.editor.default) defaults[fieldDef.code] = fieldDef.editor.default;\n });\n $scope.item.$applyDefaults(defaults);\n\n $scope.visibleFields = configService.ItemFields.filter(function(fieldDef){ return fieldDef.visibility; });\n $scope.itemTemplates = kwApi.ItemTemplate.\n getList( { branchcode: userService.login_branch }).\n sort(function(a,b){ return a.name.localeCompare(b.name);});\n\n $scope.applyTmpl = function(tmpl){\n if(tmpl) $scope.item.$applyDefaults( tmpl.template );\n else $scope.item.$applyDefaults( defaults );\n };\n $scope.save = function(){\n var minimal_item = {};\n // # flatten custom fields .\n angular.forEach($scope.item, function(v,k){\n if( configService.ItemFields.find( function(f){ return f.code==k && f.editor.readonly; })){\n return; // next;\n } // should prob also skip if no marc subfield defined.\n if(k=='fields'){\n angular.forEach(v, function(fv, fk){\n if(fv) minimal_item[fk] = fv;\n });\n } else {\n if(v) minimal_item[k] = v;\n }\n });\n\n $uibModalInstance.close(minimal_item);\n };\n }],\n windowClass: \"modal import-item-modal\",\n size: 'md',\n });\n return modalInstance.result;\n }\n };\n\n}])\n\n.controller('StaffCtrl', [\"$scope\", \"$state\", \"userService\", \"$timeout\", \"$location\", \"alertService\", \"kohaSearchSvc\", \"widgetIdx\", \"widgetList\", \"$rootScope\", \"staffLegacyApp\", \"$sce\", \"$http\", \"SearchQuery\", \"bibService\", \"kohaDlg\", \"bvStaffDlg\", \"$uibModal\", \"bvSearchToPick\", \"awLogService\", function($scope, $state, userService, $timeout, $location, alertService,\n kohaSearchSvc, widgetIdx, widgetList, $rootScope, staffLegacyApp, $sce, $http, SearchQuery,\n bibService, kohaDlg, bvStaffDlg, $uibModal, bvSearchToPick, awLogService) {\n var params = $location.search();\n $scope.isPrintView = (params && params.wack_view == 'print');\n if ($scope.isPrintView) {\n $timeout(function() { \n window.print();\n }, 1000);\n }\n\n $scope.setDefaultWidgetColumns = function() {\n $scope.widgetColumns = [\n [\n 'staff.circ', 'staff.patrons.checkout-search', 'staff.circ.checkin-search', 'staff.circ.transfers',\n 'staff.patrons.search',\n 'staff.search-title', 'staff.catalog-search', 'staff.lists',\n ], [\n 'staff.marced.home', 'staff.tools.batch-item-edit', 'staff.serials', 'staff.authorities', \n 'staff.acquisitions', 'staff.reports', 'staff.admin', 'staff.admin.sysprefs', 'staff.tools', 'staff.about'\n ], [\n ]\n ];\n };\n\n $scope.populateWidgetDock = function() {\n var isUsed = {};\n angular.forEach($scope.widgetColumns, function(col) {\n angular.forEach(col, function(widget) {\n isUsed[widget] = true;\n });\n });\n $scope.unusedStaffWidgets = jQuery.grep(widgetList,function(w) { return !isUsed[w]; });\n }\n\n if (userService.merged_prefs.staff_home_widgets) {\n $scope.widgetColumns = userService.merged_prefs.staff_home_widgets;\n }\n else {\n $scope.setDefaultWidgetColumns();\n }\n $scope.populateWidgetDock();\n $scope.oldWidgetColumns = angular.copy($scope.widgetColumns);\n\n\n $scope.configIsDirty = false;\n $scope.$watch('widgetColumns', function(newVal, oldVal) {\n if (newVal && oldVal && !angular.equals(newVal, $scope.oldWidgetColumns)) {\n $scope.configIsDirty = true;\n }\n }, true);\n\n $scope.cancelConfig = function() {\n $scope.widgetColumns = angular.copy($scope.oldWidgetColumns);\n $scope.configIsDirty = false;\n };\n\n $scope.saveConfig = function() {\n userService.addPrefs({staff_home_widgets: $scope.widgetColumns}).then(function() {\n $scope.configIsDirty = false;\n $scope.oldWidgetColumns = angular.copy($scope.widgetColumns);\n });\n };\n\n $scope.resetConfig = function() {\n $scope.setDefaultWidgetColumns();\n $scope.configIsDirty = true;\n $scope.populateWidgetDock();\n $scope.oldWidgetColumns = angular.copy($scope.widgetColumns);\n };\n\n $scope.widget = widgetIdx;\n $scope.user = userService;\n $scope.simpleSearch = kohaSearchSvc.doSimpleSearch;\n $scope.showSidebar = true;\n\n $scope.showWidget = function(widget) {\n if (widget.permissions) {\n if (!userService.can(widget.permissions)) {\n return false;\n }\n }\n if (widget.displayIf) {\n if (!widget.displayIf())\n return false;\n }\n return true;\n };\n\n\n $scope.splitColumns = function(ary,cols) {\n var n = Math.ceil(ary.length / cols);\n var rv = [];\n var i;\n for (i=0; i sortKeys.includes(key));\n const sortedObj = Object.fromEntries(\n keys.sort((a, b) => sortKeys.indexOf(a) - sortKeys.indexOf(b))\n .map(key => [key, data[key]])\n );\n d.reg_data = sortedObj;\n });\n });\n }\n\n $scope.rejectApp = function (d) {\n patronRegSvc.rejectAppSvc(d).then(function() {\n $scope.syncApps();\n });\n }\n\n $scope.approveApp = function (d) {\n patronRegSvc.approveAppSvc(d).then(function() {\n $scope.syncApps();\n const qs = new URLSearchParams(d.reg_data).toString();\n $window.open(\"/app/staff/patrons/addnew?op=add¬ify=1&\" + qs);\n });\n }\n\n $scope.syncApps();\n}])\n\n.controller('StaffMarcBatchEditCtrl', [\"$scope\", \"batches\", \"importBatchService\", \"$uibModal\", function($scope, batches, importBatchService, $uibModal) {\n var batchSVC = importBatchService;\n $scope.order = batchSVC.sort;\n $scope.pager = batchSVC.pager;\n $scope.currentPage = 1;\n\n $scope.pageChanged = function () {\n $scope.pager.setPage($scope.currentPage);\n batchSVC.toPage($scope.currentPage);\n }\n\n $scope.$watch('order', function(newVal) {\n if (newVal) {\n batchSVC.getList();\n }\n }, true);\n\n $scope.classOf = function(n) {\n return 'import-batch-' + n.import_status;\n };\n\n // We don' need no steenkin ... oh, wait, actually we do\n $scope.batches = batches;\n\n $scope.canFilter = function(n) {\n return (n.import_status === 'staged');\n };\n\n $scope.filter = function(n) {\n $uibModal.open({\n backdrop: true,\n templateUrl: '/app/static/partials/staff/tools/marc-batch-edit/filter.html',\n controller: 'StaffMarcBatchEditFilterCtrl', // ew\n windowClass: 'modal',\n resolve: {\n batch: function() { return n; },\n berts: [\"bertService\", function(bertService) {\n return bertService.getList();\n }],\n }\n });\n };\n}])\n\n.controller('StaffMarcBatchEditFilterCtrl', [\"$scope\", \"batch\", \"berts\", \"$uibModalInstance\", \"importBatchService\", \"alertService\", function($scope, batch, berts, $uibModalInstance, importBatchService, alertService) {\n $scope.batch = batch;\n $scope.filters = [{}];\n $scope.berts = berts;\n\n $scope.filterSelectConfig = {\n maxItems: 1,\n valueField: 'id',\n labelField: 'title',\n searchField: ['title','description'],\n create: false,\n render: {\n item: function(rec, escape) {\n if (!rec) return '';\n return '
' + escape(rec.title) + ' - ' + escape(rec.description) + '
';\n },\n option: function(rec, escape) {\n if (!rec) return '';\n return '
' + escape(rec.title) + ' - ' + escape(rec.description) + '
';\n },\n },\n };\n\n $scope.rmFilter = function(idx) {\n $scope.filters.splice(idx,1);\n };\n\n $scope.addFilter = function(idx) {\n $scope.filters.push({});\n };\n\n $scope.canStart = function() {\n var ok = true;\n if (!$scope.filters.length)\n return false;\n $scope.filters.forEach(function(f) {\n if (!(f.selected))\n ok = false;\n });\n return ok;\n };\n\n $scope.mode = {async: 1};\n\n $scope.start = function() {\n var filter_ids = $scope.filters.map(function(f) { return f.selected; });\n importBatchService.filter(batch, filter_ids, $scope.mode.async).then(function() {\n $uibModalInstance.close();\n if (1 == $scope.mode.async) \n alertService.add({msg: \"Import batch edit started; you will be notified when it is complete\", type: \"info\"});\n else \n alertService.add({msg: \"Batch edit complete\", type: \"info\"});\n }, function(err) {\n alertService.addApiError(err,'Can\\'t edit import batch');\n $uibModalInstance.close();\n });\n };\n}])\n\n.controller('StaffCatalogSearchCtrl', [\"$scope\", \"$injector\", \"configService\", \"$state\", \"$timeout\", \"$rootScope\", \"SearchQuery\", \"kohaSearchSvc\", function($scope, $injector, configService, $state, $timeout, $rootScope,\n SearchQuery, kohaSearchSvc){\n $scope.inputQuery = {q : \"\"};\n $scope.clearMeOut = function(){\n if(configService.geospatialSearch && ARCHVIEW.mapLayers && ARCHVIEW.mapLayers.length > 0){\n var mapComptrollerSvc = $injector.get('mapComptrollerSvc');\n if(mapComptrollerSvc) mapComptrollerSvc.clearAOIAndDontSearch();\n $rootScope.$broadcast(\"triggerClearAll\");\n }\n $scope.inputQuery.q = \"\";\n kohaSearchSvc.clearCurrentSearch();\n// $state.go(\"search\", undefined, {inherit:false});\n };\n\n $scope.$on('$stateChangeSuccess', function () {\n if($state.includes('search-results')){\n $timeout(function(){\n var curSrch = kohaSearchSvc.currentSearch();\n if(curSrch){\n var q = curSrch.q;\n if(/^\\([^\\(\\)]+\\)$/.test(q)){\n q = q.replace(/^\\(|\\)$/g, '');\n }\n $scope.inputQuery.q = q;\n }\n });\n } else {\n $scope.inputQuery.q = \"\";\n }\n });\n $scope.$on('clearUserData', function() {\n $scope.inputQuery.q = '';\n });\n\n $scope.submitSearch = function(){\n var search = new SearchQuery({q: $scope.inputQuery.q});\n kohaSearchSvc.doSimpleSearch(search);\n };\n}])\n\n.controller('StaffCallNumberBrowseCtrl', [\"$scope\", \"userService\", \"kwApi\", \"loading\", \"Pager\", \"bvDownloadSvc\", function($scope, userService, kwApi, loading, Pager, bvDownloadSvc){\n $scope.results = [];\n $scope.browse = {\n branch: null,\n ccode: null,\n callnumber: null,\n };\n $scope.ccodes_list = $scope.config.listCodes('ccode');\n $scope.browse.ccode = $scope.ccodes_list[0];\n var pagelength = 100;\n var filterResults = null;\n $scope.pageData = [];\n $scope.page = 1;\n\n $scope.pageChanged = function (page) {\n var begin = ((page - 1) * pagelength);\n var end = begin + pagelength;\n if ($scope.search) {\n $scope.pageData = filterResults.slice(begin, end);\n }\n else {\n $scope.pageData = $scope.results.slice(begin, end);\n }\n }\n\n $scope.filterSearch = function () {\n filterResults = $scope.results.filter(function(i) {\n var re = new RegExp(\"^\" + $scope.search, \"i\");\n if (re.test(i.callnumber)) return i;\n });\n $scope.pager = new Pager({count: filterResults.length, pagelength: pagelength});\n var begin = 0;\n var end = begin + pagelength;\n $scope.pageData = filterResults.slice(begin, end);\n }\n\n userService.getAccessibleBranchesAndGroups('circulate.circulate_remaining_permissions').then(function(b) {\n $scope.branches_list = b.branches;\n $scope.branches_list.forEach( function (branch) {\n if (branch.branchcode == userService.login_branch) {\n $scope.browse.branch = branch;\n $scope.browse.fetch();\n }\n });\n }, function(e) {\n console.error(\"Branch list loading error in StaffCallNumberBrowseCtrl: \" + e);\n });\n\n $scope.browse.fetch = function() {\n loading.add();\n $scope.page = 1;\n kwApi.Item.getCnBrowse({branch: $scope.browse.branch.branchcode, ccode: $scope.browse.ccode},\n function success(d) {\n $scope.results = [];\n d.forEach(function(i) {\n $scope.results.push({biblionumber: i[2], itemnumber: i[0], callnumber: i[1] ? i[1] : '', bibtitle: i[4], barcode: i[3]});\n });\n if ($scope.search) {\n $scope.filterSearch();\n }\n else {\n $scope.pager = new Pager({count: $scope.results.length, pagelength: pagelength});\n }\n $scope.pageChanged(1);\n loading.resolve();\n },\n function fail(e) {\n console.error(e);\n loading.resolve();\n }\n );\n }\n\n let getTheData = () => {\n let output = \"\\\"callnumber\\\",\\\"itemnumber\\\",\\\"biblionumber\\\",\\\"barcode\\\",\\\"title\\\"\\n\";\n for (var i = 0; i < $scope.pageData.length; i++) {\n output += \"\\\"\" + $scope.pageData[i]['callnumber'] + \"\\\",\";\n output += \"\\\"\" + $scope.pageData[i]['itemnumber'] + \"\\\",\";\n output += \"\\\"\" + $scope.pageData[i]['biblionumber'] + \"\\\",\";\n output += \"\\\"\" + $scope.pageData[i]['barcode'] + \"\\\",\";\n output += \"\\\"\" + $scope.pageData[i]['bibtitle'].replace( /\\\"/g, '\"\"' ) + \"\\\",\"; // double-quote is the escape character\n output += \"\\n\";\n }\n return output;\n };\n\n $scope.fetchFile = () => {\n bvDownloadSvc.fetch({fetchData: getTheData, fileName: 'cn_browse.csv'});\n };\n}])\n\n.controller('StaffCircCheckinCtrl', [\"$scope\", \"$http\", \"$uibModal\", \"$timeout\", \"$stateParams\", \"$state\", \"kwApi\", \"configService\", \"userService\", \"alertService\", \"kwBeepSvc\", \"$location\", function($scope, $http, $uibModal, $timeout, $stateParams, $state, kwApi, configService, userService, alertService, kwBeepSvc,\n $location) {\n\n $scope.checkin_params = { barcode: '', exemptfine: false, date_override: null, dropbox: false };\n\n $scope.datePickOpts = {\n minDate: '1970-01-01',\n maxDate: new Date()\n };\n $scope.checkin_history = []; // FIXME: This should probably persist beyond controller.\n\n var max_history_length = 50;\n\n $scope.checkins = {\n list: [], // ordered.\n history: {}, // by item_id.\n add: function(checkin){\n // add or replace item.\n if(!checkin.item)return;\n var item_id = checkin.item.id;\n this.list = this.list.filter(function(chk){ return (chk.item.id != item_id); });\n this.list.unshift(checkin);\n this.history[checkin.item.id] = checkin;\n if(this.list.length > max_history_length){\n var self = this;\n var removed = this.list.splice( max_history_length );\n removed.forEach(function(checkin){ delete self.history[checkin.item.id]; });\n }\n },\n\n };\n\n $scope.soundon = configService.soundon;\n\n $scope.visible_history = configService.numReturnedItemsToShow || 10;\n\n $scope.messages = [];\n\n $scope.cfg = configService;\n\n var bcel = $(\"#checkinBarcode\");\n $scope.returnFocus = function(test_view){\n // FIXME: EWW.\n if(test_view){ // i.e. model doesn't update unless it's a valid date.\n if( !$scope.date_override ) return;\n }\n\n bcel.focus();\n };\n bcel.focus();\n $scope.toggleDropbox = function(){\n // Note we dropbox date is dependent on item's circControl branch, so we don't prefetch.\n $scope.checkin_params.dropbox = !$scope.checkin_params.dropbox;\n $scope.returnFocus();\n };\n\n var thisbranch = userService.login_branch;\n\n $scope.clear = function(){\n $scope.messages = [];\n $scope.checkin_params.barcode = '';\n $scope.transfer = undefined;\n $scope.tabsearch.checkin.barcode = '';\n };\n\n $scope.doCheckin = function(item_id){\n\n if(!$scope.checkin_params.barcode) return;\n\n var params = {\n barcode: $scope.checkin_params.barcode,\n exemptfine: $scope.checkin_params.exemptfine,\n dropbox: $scope.checkin_params.dropbox\n };\n if($scope.checkin_params.date_override) params.date_override = $scope.checkin_params.date_override.toISOString();\n\n $scope.clear();\n\n kwApi.Item.checkin({ id: item_id }, params,\n function success(item){\n\n var sideband = angular.copy(item._embed);\n delete item._embed;\n $scope.messages = angular.copy(sideband.notices);\n\n var checkin = {\n item: item,\n issue: sideband.issue_summary, // may be undefined.\n bib_summary: sideband.bib_summary,\n hold: undefined,\n messages: angular.copy(sideband.notices),\n patron: undefined,\n checkin_note: undefined\n };\n\n if (sideband.checkin_note)\n checkin.checkin_note = new kwApi.ItemCheckinNote( sideband.checkin_note );\n if (sideband.trapped_hold)\n checkin.hold = new kwApi.Hold( sideband.trapped_hold );\n if (sideband.issue_summary)\n checkin.overdue = dayjs(sideband.issue_summary.duedate).isBefore();\n\n $scope.checkins.add( checkin );\n\n var alert_tone = 'beep';\n\n if(checkin.checkin_note){\n alert_tone = 'warn';\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/circ/item-checkin-note-modal.html',\n animation: false,\n // keyboard: false,\n size: 'sm',\n windowClass: 'item-checkin-note',\n backdrop: 'static',\n resolve: {\n checkin_note: function(){ return checkin.checkin_note; },\n item: function(){ return item; },\n checkin: function(){ return true; }\n },\n controller: 'ItemCheckinNoteCtrl'\n });\n }\n\n if(item.in_transit){\n $scope.transfer = {\n to: item.in_transit.to,\n bibid: item.biblionumber,\n title: checkin.bib_summary.title,\n };\n alert_tone = 'warn';\n }\n $scope.cancelTransfer = function(){\n item.$cancelTransfer().then(function(rsp){\n console.log(rsp);\n $scope.clear();\n $scope.checkins.history[item.id].item.in_transit = undefined;\n alertService.add({ msg: \"Transfer cancelled.\", type: \"success\"});\n });\n };\n\n if(checkin.hold){\n // hold-transit modal...\n // note that actions taken in this modal can affect the parent item.\n // if it's on hold, we need confirm hold and possibly initiate xfer.\n\n var checkinScope = $scope;\n alert_tone = 'alert';\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/circ/hold-transfer-modal.html',\n animation: false,\n // keyboard: false,\n backdrop: 'static',\n controller: [\"$scope\", function($scope) {\n $scope.cfg = configService;\n $scope.checkin = checkin;\n $scope.send_to = (item.in_transit||{}).to;\n $scope.today = new Date();\n if(checkin.hold){\n kwApi.Patron.get( {id: checkin.hold.borrowernumber}, function(p){ $scope.patron = p; });\n if(checkin.hold.branchcode != thisbranch){\n $scope.send_to = checkin.hold.branchcode;\n }\n }\n $scope.confirmHold = function(print_slip) {\n checkin.hold.$confirm_fill( { item_id: item.id } ).then(function success(rsp){\n if(checkin.hold.branchcode != thisbranch){\n // confirming hold initiated a transfer.\n //FIXME: should probably just refresh item from server; this transfer info is incomplete\n checkinScope.checkins.history[item.id].item.in_transit = { to : checkin.hold.branchcode };\n\n }\n // ELSE - FIXME: we need to show that the item is marked to fill hold...\n // either message 'filling local hold' or 'transfer to ....'\n\n if (print_slip) {\n checkin.hold.$print_slip( { send_to: !!$scope.send_to } ).then(function success(rsp){\n var utf8Data = decodeURIComponent(escape(rsp.data));\n var printWindow = window.open();\n printWindow.document.write(utf8Data);\n printWindow.document.close();\n printWindow.print();\n }, function err(e){\n console.error(\"Failed to print hold slip: \" + e.data);\n alertService.addApiError(e,'Failed to print hold slip');\n });\n }\n }, function err(e){\n alertService.addApiError(e,'Failed to confirm hold');\n });\n $scope.$close();\n };\n\n $scope.ignoreHold = function(){\n if(checkin.hold.found){\n checkin.hold.$requeue();\n checkinScope.messages.push({ code: 'hold_requeued'});\n }\n else if(checkin.hold){\n checkinScope.messages.push({ code: 'unhandled_hold'});\n }\n $scope.$close();\n };\n }],\n });\n } // endif checkin.hold\n\n\n if(checkin.issue){\n checkin.issue.patron_display = checkin.issue.borrowernumber;\n kwApi.Patron.get( { id: checkin.issue.borrowernumber }, function(p){\n checkin.issue.patron_display = p.surname + ', ' + p.firstname + ' (' + p.categorycode + ')';\n checkin.issue.patron_note = p.borrowernotes;\n });\n } else {\n $scope.messages.push({ code: 'not_issued'} );\n }\n\n if($scope.soundon) kwBeepSvc.play(alert_tone);\n\n }, function fail(rsp){\n\n if(angular.isObject(rsp.data)){\n\n if(rsp.data.lost_item){\n // staff must handle lost_item, can resubmit afterwards.\n var lostItem = new kwApi.LostItem( rsp.data.lost_item );\n var checkinScope = $scope;\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/circ/lost-item-modal.html',\n animation: false,\n keyboard: false,\n backdrop: 'static',\n controller: [\"$scope\", function($scope) {\n\n // FIXME: duplicated in circ-status controller.\n $scope.context = 'checkin';\n $scope.loserName = lostItem.borrowernumber;\n kwApi.Patron.get({id: lostItem.borrowernumber}, function(patron){\n $scope.loser = patron;\n $scope.loserName = patron.surname + ', ' + patron.firstname;\n });\n\n $scope.canHandleLostItem = userService.can({borrowers: 'delete_lost_items'});\n $scope.canRefundLostItem = configService.RefundReturnedLostItem;\n $scope.lost = {\n remove: !!$scope.canHandleLostItem,\n refund: ($scope.canRefundLostItem) ? 'LOSTRETURNED' : ''\n };\n\n $scope.handleLost = function() {\n\n lostItem.$checkin( {\n id: lostItem.id,\n remove: $scope.lost.remove,\n refund: $scope.lost.refund\n }).then(function success(li_rsp){\n checkinScope.checkin_params.barcode = lostItem.barcode;\n checkinScope.doCheckin(lostItem.itemnumber);\n }, function err(e){\n alertService.addApiError(e,'Failed to handle lost item');\n });\n $scope.$close();\n };\n\n $scope.cancel = function(){\n checkinScope.messages.push({ code: 'checkin_fail'});\n $scope.$close();\n };\n }],\n });\n }\n } else {\n if(rsp.status==404){\n $scope.messages = [ { code: 'not_found'} ];\n } else if(/^403 Item is withdrawn/.test(rsp.data)){\n $scope.messages = [ { code: 'withdrawn'} ];\n }\n alertService.addApiError(rsp,'Checkin failed');\n\n bcel.focus();\n }\n if($scope.soundon) kwBeepSvc.play('warn');\n }\n );\n bcel.focus();\n };\n\n if($stateParams.barcode){\n $scope.checkin_params.barcode = $stateParams.barcode;\n $scope.doCheckin();\n $location.path('/app/staff/circ/checkin/').replace(); // param barcode is marked 'dynamic', the view is NOT updated when the URL is changed\n }\n\n}])\n\n.controller('ItemCheckinNoteCtrl', [\"$scope\", \"checkin_note\", \"item\", \"checkin\", \"userService\", \"kwApi\", \"alertService\", function($scope, checkin_note, item, checkin, userService, kwApi, alertService){\n\n $scope.checkin_note = checkin_note;\n\n $scope.editable = !checkin && userService.can({circulate: {item_checkin_notes: 'modify'}});\n $scope.deletable = userService.can({circulate: {item_checkin_notes: 'delete'}});\n\n if (!checkin_note) {\n $scope.checkin_note = new kwApi.ItemCheckinNote({item_id: item.id});\n $scope.deletable = false;\n }\n else if (checkin_note.$promise) {\n checkin_note.$promise.then(\n function(){ },\n function(){ // assume 404.\n $scope.checkin_note = new kwApi.ItemCheckinNote({item_id: item.id});\n $scope.deletable = false;\n });\n }\n else { // note was embedded in item\n checkin_note.$resolved = true;\n }\n\n $scope.saveNote = function(){\n if(!$scope.editable) return;\n var method = ($scope.checkin_note.$resolved) ? 'update' : 'save';\n kwApi.ItemCheckinNote[method]($scope.checkin_note).$promise.then(function(note) {\n alertService.add({msg: 'Checkin note saved.', type: 'success'});\n $scope.checkin_note = note;\n $scope.$close($scope.checkin_note);\n }, function(err) {\n alertService.addApiError(err,'Failed to save note');\n $scope.$close();\n });\n };\n $scope.deleteNote = function(){\n if(!$scope.deletable) return;\n kwApi.ItemCheckinNote.delete($scope.checkin_note).$promise.then(function() {\n delete $scope.checkin_note.note;\n $scope.checkin_note.$resolved = false;\n });\n $scope.$close($scope.checkin_note);\n };\n\n}])\n\n.controller('StaffCircOfflineCtrl', [\"$scope\", \"loading\", \"kwApi\", \"configService\", \"alertService\", \"userService\",\n function($scope, loading, kwApi, configService, alertService, userService) {\n\n $scope.extActive = false;\n\n function clearCirc (){\n console.log('clearing data');\n window.postMessage({\n type: 'offline-circ-app',\n do: 'clear-circ'\n }, '*');\n }\n\n $scope.extension = {\n config: null,\n checkouts: null\n };\n $scope.checkouts = [];\n\n window.addEventListener(\"message\", receiveMessage, false);\n\n function receiveMessage(event) {\n\n var msg = event.data;\n if( event.source != window || msg.type !='offline-circ-ext' ) return;\n // msg.do = 'data' is the only msg we receive anymore.\n\n if(msg.do == 'data'){\n\n $scope.$apply( function(){\n $scope.extActive = true;\n $scope.extension.config = msg.data.config || {};\n $scope.checkouts = msg.data.checkouts || [];\n $scope.noData = !$scope.checkouts.length\n });\n\n // if no config data, send it.\n if( !msg.data.config || \n msg.data.config.libraryName != $scope.extension.config.libraryName ||\n msg.data.config.hostname != $scope.extension.config.hostname\n ){\n window.postMessage({\n type: 'offline-circ-app',\n do: 'set-config',\n data: {\n libraryName: configService.pageTitle,\n hostname: window.location.host\n }\n }, '*');\n }\n\n }\n }\n\n $scope.$watch('checkouts', function(checkouts){\n if(checkouts){\n var map = {};\n checkouts.forEach(function(co){\n map[co.patron]++;\n });\n $scope.patronCount = Object.keys(map).length;\n }\n });\n\n $scope.clearAll = function(){\n $scope.synced = undefined;\n $scope.checkouts = [];\n };\n\n $scope.doCheckouts = function(){\n\n $scope.synced = {\n failed: [],\n patronCount: 0,\n issued: [],\n complete: false\n };\n var seenPatron = {};\n loading.add( 'processing_offline_circ' );\n var unwatch = $scope.$watch('synced.complete', function(done){\n if(!done) return;\n loading.resolve( 'processing_offline_circ' );\n alertService.add({\n msg: 'Offline Circulation upload complete. ' + $scope.synced.issued.length +\n ' Successful checkouts cleared.',\n type: 'success'});\n // optionally clear failures?\n // todo: allow edits of failed checkouts and resubmittal.\n // automatically clear successes.\n\n $scope.checkouts = [];\n clearCirc();\n unwatch();\n });\n\n $scope.checkouts.forEach(function(co){\n kwApi.Item.offlineCheckout({\n barcode: co.item,\n cardnumber: co.patron,\n ts: co.ts\n }, function(item){\n console.log(item);\n $scope.synced.issued.push(co);\n if(!seenPatron[co.patron]) $scope.synced.patronCount++;\n seenPatron[co.patron] = true;\n if($scope.synced.issued.length + $scope.synced.failed.length == $scope.checkouts.length)\n $scope.synced.complete = true;\n }, function(fail){\n console.warn(fail);\n var error = fail.data.replace(/^\\d\\d\\d ?/,'');\n if(error==\"Not Found\") error = \"Barcode not found\";\n $scope.synced.failed.push( {\n patron: co.patron,\n item: co.item,\n ts: co.ts,\n error: error\n });\n if($scope.synced.issued.length + $scope.synced.failed.length == $scope.checkouts.length)\n $scope.synced.complete = true;\n });\n\n });\n\n };\n\n $scope.dlErrors = function(){\n var output = \"\\nPatron cardnumber, Item barcode, Timestamp, Error\\n\\n\";\n $scope.synced.failed.forEach(function(f){\n output += f.patron + \", \" + f.item + \", \" + f.ts + \", \" + f.error + \"\\n\";\n });\n window.open('data:text/csv;charset=utf-8,' + escape(output));\n };\n $scope.dlSuccess = function(){\n var output = \"\\nPatron cardnumber, Item barcode, Timestamp\\n\\n\";\n $scope.synced.issued.forEach(function(f){\n output += f.patron + \", \" + f.item + \", \" + f.ts + \"\\n\";\n });\n window.open('data:text/csv;charset=utf-8,' + escape(output));\n };\n\n function getExtension() {\n var url;\n if(window.navigator.userAgent.match('Firefox')){\n url = \"https://addons.mozilla.org/addon/bibliovation-offline-circ/\";\n } else if(window.chrome){\n url = \"https://chrome.google.com/webstore/detail/lnihpohmbofgfebfamjgkhiofkpfifdj\";\n } else {\n alert(\"Please use Chrome or Firefox to use this feature.\");\n return;\n }\n window.open(url, '_blank');\n }\n $scope.goStore = getExtension;\n\n\n}])\n\n// Reports\n\n.controller('StaffReportBaseCtrl', [\"$scope\", \"$state\", \"userService\", \"loading\", \"alertService\", \"kwApi\", \"$uibModal\", \"$q\", function($scope, $state, userService, loading, alertService, kwApi, $uibModal, $q) {\n // context container\n $scope.c = {\n showPager: true,\n page: {\n start: 0,\n count: 10,\n page: 1,\n },\n order: {\n field: 'date_created',\n reverse: true,\n },\n filterTags: [],\n };\n\n $scope.readonly = userService.can({reports: 'update'}) ? false : true;\n\n if (userService.merged_prefs.sqlreports_numresults) {\n $scope.c.page.count = userService.merged_prefs.sqlreports_numresults;\n }\n\n $scope.report_types = [\n { label: 'Tabular', value: 'Tabular' },\n { label: 'Matrix', value: 'Matrix' },\n ];\n\n $scope.matrix_sort_types = [\n { label: 'Alphabetical', value: 'alpha' },\n { label: 'Numeric', value: 'numeric' },\n ];\n\n $scope.runReport = function(report, schedule, email) {\n loading.add();\n kwApi.Report.getParameters({id: report.id}).$promise.then(function(parameters) {\n loading.resolve();\n var p;\n if (parameters.length) {\n p = $uibModal.open({\n templateUrl: '/app/static/partials/staff/reports/parameters.html',\n windowClass: 'modal',\n controller: [\"$scope\", function($scope) {\n $scope.parameters = parameters;\n $scope.data = {};\n $scope.close = function() {\n $scope.$close($scope.data);\n };\n $scope.cancel = function() {\n $scope.$close(undefined);\n };\n }],\n }).result;\n }\n else {\n p = $q.when({});\n }\n\n p.then(function(paramValues) {\n if (paramValues === undefined) return;\n\n loading.add();\n var op = (schedule ? 'schedule' : 'execute');\n kwApi.Report[op]({\n id: report.id, \n }, {\n when: schedule, parameters: paramValues, email: email\n }).$promise.then(function(rv) {\n loading.resolve();\n alertService.add({msg: \"Report \" + (schedule ? \"scheduled\" : \"started\") + \"; you will be notified when it is complete\", type: 'info'});\n $state.reload();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err, \"Unable to \" + (schedule ? \"schedule\" : \"start\") + \" report\");\n $state.reload();\n });\n });\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err, \"Unable to get report parameters\");\n });\n };\n\n $scope.scheduleReport = function(report) {\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/reports/schedule.html',\n size: 'lg',\n controller: [\"$scope\", function($scope) {\n $scope.data = {type: 'at', cron: '0:0:0:1*0:0:0'};\n if ((report._embed || {}).scheduled) {\n if (report._embed.scheduled.cron_spec) {\n $scope.data.type = 'repeat';\n $scope.data.cron = report._embed.scheduled.cron_spec;\n }\n else if (report._embed.scheduled.scheduled_time) {\n $scope.data.type = 'at';\n var at = report._embed.scheduled.scheduled_time.split(' ');\n $scope.data.atDate = at[0];\n $scope.data.atTime = at[1];\n }\n }\n\n $scope.isValid = function() {\n $scope.err = false;\n if ($scope.data.type === 'repeat') {\n if (!$scope.data.cron) {\n $scope.err = 'No repeat interval defined';\n return false;\n }\n }\n else if ($scope.data.type === 'at') {\n if (!$scope.data.atDate) {\n $scope.err = 'No scheduled date defined';\n return false;\n }\n if (!$scope.data.atTime) {\n $scope.err = 'No scheduled time defined';\n return false;\n }\n if (! /^\\d\\d:\\d\\d:\\d\\d$/.test($scope.data.atTime)) {\n $scope.err = 'Invalid time (use HH:MM:SS)';\n return false;\n }\n }\n if ($scope.data.email) {\n if (!$scope.data.recipient) {\n $scope.err = 'No recipient email address defined';\n return false;\n }\n }\n return true;\n };\n }],\n }).result.then(function(rv) {\n if (rv) {\n var obj = {};\n if (rv.type === 'repeat') {\n obj.cron = rv.cron;\n }\n else {\n obj.at = dayjs(rv.atDate).format().substr(0,10) + ' ' + rv.atTime;\n }\n var email;\n if (rv.email === true) {\n email = rv.recipient;\n }\n return $scope.runReport(report, obj, email);\n }\n });\n };\n}])\n\n.controller('StaffReportIndexCtrl', [\"$scope\", \"reports\", \"report_tags\", \"kwApi\", \"loading\", \"$uibModal\", \"alertService\", \"kohaDlg\", \"userService\", function($scope, reports, report_tags, kwApi, loading, $uibModal, alertService, kohaDlg, userService) {\n\n $scope.c = { // should be unneeded but sandbox throws `TypeError: c is undefined` from restoreView\n showPager: true,\n page: {\n start: 0,\n count: 10,\n page: 1,\n },\n order: {\n field: 'date_created',\n reverse: true,\n },\n filterTags: [],\n };\n const pidRegex = new RegExp('^pid:\\\\d+$');\n const tagID = 'pid:' + userService.id;\n $scope.username = userService.username;\n $scope.reports = reports;\n $scope.reports.forEach(function(n){\n n._embed.tags.find(function(elm, idx) {\n if (elm === tagID) n._embed.tags[idx] = 'My list';\n });\n n._embed.tags = n._embed.tags.filter(function(t) {\n return !pidRegex.test(t);\n });\n });\n $scope.tags = report_tags.filter(function(t) {\n return !pidRegex.test(t.name);\n });\n\n $scope.selected = {};\n $scope.displayReports = [];\n $scope.totalFiltered = 0;\n\n $scope.refresh = function(clearSelection) {\n // filter by tags\n var displayList;\n var filterTags = $scope.c.filterTags;\n if (filterTags.length) {\n displayList = reports.filter(function(r) {\n var hasTag = {};\n (r._embed.tags || []).forEach(function(tag) {\n hasTag[tag] = true;\n });\n for (var i=0; i b[ordf].toUpperCase()) {\n return n;\n }\n else {\n return 0;\n }\n });\n displayList = $scope.c.showPager\n ? displayList.slice($scope.c.page.start, $scope.c.page.start + (1*$scope.c.page.count))\n : displayList;\n\n $scope.displayReports.replaceWith(displayList);\n\n if (clearSelection)\n $scope.selected = {};\n };\n\n $scope.pageChanged = function() {\n $scope.c.page.start = ($scope.c.page.page-1) * $scope.c.page.count;\n $scope.refresh();\n };\n\n $scope.sortChanged = function() {\n $scope.c.page.start = 0;\n $scope.c.page.page = 1;\n $scope.refresh();\n };\n\n $scope.$watch('c.order', function(newVal) {\n if (!newVal) return;\n $scope.sortChanged();\n }, true);\n\n $scope.tagsChanged = function() {\n $scope.c.page.start = 0;\n $scope.c.page.page = 1;\n $scope.refresh(true);\n };\n\n $scope.saveView = () => {\n let view = { filter_tags: $scope.c.filterTags, page_order: $scope.c.order };\n userService.setPref(\"reports_view_options\", view);\n alertService.add({msg: \"View options saved\", type: \"info\"});\n }\n\n $scope.restoreView = () => {\n let view = userService.getPref(\"reports_view_options\")||{};\n view.filter_tags && view.filter_tags.forEach((i) => {\n $scope.addFilterTag(i);\n });\n if (view.page_order) $scope.c.order = view.page_order;\n }\n\n $scope.addFilterTag = function(tag) {\n if (!$scope.c.filterTags.includes(tag)) {\n $scope.c.filterTags.push(tag);\n $scope.tagsChanged();\n }\n };\n\n $scope.removeFilterTag = function(tag) {\n $scope.c.filterTags = $scope.c.filterTags.filter(function(t) { return (t != tag) });\n $scope.tagsChanged();\n };\n\n $scope.batchToggleMyTag = function() {\n $scope.batchToggleTag(tagID);\n }\n\n $scope.batchToggleTag = function(tag, isNew) {\n loading.add();\n var op;\n if (isNew) {\n op = 'add-tag';\n }\n else {\n op = 'delete-tag';\n $scope.reports.forEach(function(r) {\n if ($scope.selected[r.id] && !r._embed.tags.includes(tag)) {\n const tagIDRegex = new RegExp('^' + tagID + '$');\n if (!(r._embed.tags.includes('My list') && tagIDRegex.test(tag))) {\n op = 'add-tag';\n }\n }\n });\n }\n\n var ids = Object.keys($scope.selected).filter(function(k) { return $scope.selected[k] });\n kwApi.Report.forAll({action: op, ids: ids, tag: tag}).$promise.then(function() {\n loading.resolve();\n $scope.reload();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Unable to apply tags');\n });\n if ($scope.c.filterTags.length) $scope.selected = {};\n };\n\n $scope.batchAddNewTag = function() {\n var origScope = $scope;\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/reports/new-tag.html',\n windowClass: 'modal',\n controller: [\"$scope\", function($scope) {\n $scope.pattern = /^(?!My list$|pid:\\d+$).*$/;\n $scope.newtag = {name: ''};\n $scope.close = function(s) {\n origScope.batchToggleTag(s, true);\n $scope.$close();\n };\n $scope.cancel = function() {\n $scope.$close();\n };\n }],\n });\n };\n\n $scope.batchClearTags = function() {\n loading.add();\n var ids = Object.keys($scope.selected).filter(function(k) { return $scope.selected[k] });\n kwApi.Report.forAll({action: 'clear-tags', ids: ids}).$promise.then(function() {\n loading.resolve();\n $scope.reload();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Unable to apply tags');\n });\n if ($scope.c.filterTags.length) $scope.selected = {};\n };\n\n $scope.batchDelete = function() {\n kohaDlg.dialog({\n heading: 'Are you sure?',\n message: 'Are you sure you want to delete these reports? This CANNOT be undone.',\n buttons: [{val: true, label: 'Yes', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n }).result.then(function(rv) {\n if (!rv) return;\n\n loading.add();\n var ids = Object.keys($scope.selected).filter(function(k) { return $scope.selected[k] });\n $scope.loading = true;\n kwApi.Report.forAll({action: 'delete', ids: ids}).$promise.then(function() {\n loading.resolve();\n $scope.reload();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Unable to apply tags');\n });\n });\n };\n\n $scope.showPager = function(v) {\n $scope.c.showPager = v;\n $scope.refresh();\n };\n\n\n $scope.total_checked = 0;\n $scope.$watch('selected', function(v) {\n var n = 0;\n angular.forEach(v, function(val, key) {\n if (val) n++;\n });\n $scope.total_checked = n;\n }, true);\n\n $scope.reload = function() {\n loading.add();\n kwApi.Report.query().$promise.then(function(r) {\n $scope.reports = reports = r;\n $scope.reports.forEach(function(n){\n n._embed.tags.find(function(elm, idx) {\n if (elm === tagID) n._embed.tags[idx] = 'My list';\n });\n n._embed.tags = n._embed.tags.filter(function(t) {\n return !pidRegex.test(t);\n });\n });\n return kwApi.ReportTag.query().$promise;\n }).then(function(t) {\n $scope.tags = report_tags = t.filter(function(x) {\n return !pidRegex.test(x.name);\n });\n loading.resolve();\n $scope.refresh();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Unable to reload list');\n $scope.refresh();\n });\n };\n\n $scope.restoreView();\n $scope.refresh(true);\n\n $scope.run = function(r) {\n r.listClass = '';\n $scope.runReport(r);\n };\n\n $scope.$on('sys.signal.Report', function(evnt, args) { \n angular.forEach($scope.reports, function(r) {\n if (r.id == args.report_id) {\n if (args.type == 'done')\n r.listClass = 'report-done';\n else\n r.listClass = 'report-failed';\n }\n });\n });\n}])\n\n.controller('StaffReportDetailsCtrl', [\"$scope\", \"report\", \"runs\", \"kwApi\", \"loading\", \"alertService\", \"userService\", \"$q\", function($scope, report, runs, kwApi, loading, alertService, userService, $q) {\n $scope.order = {\n field: 'updated_time',\n reverse: true,\n };\n\n $scope.update_report = function() {\n loading.add();\n if ($scope.report.type == 'Matrix') {\n $scope.report.metadata.matrix = $scope.matrixDef;\n }\n\n kwApi.Report.put({id: $scope.report.id}, $scope.report).$promise.then(function() {\n loading.resolve();\n alertService.add({msg: \"Report updated\", type: \"info\"});\n $scope.reload_report();\n }, function(err) {\n loading.resolve();\n $scope.loading = false;\n alertService.addApiError(err, \"Save failed\");\n $scope.reload_report();\n });\n };\n\n $scope.reload_report = function() {\n loading.add();\n kwApi.Report.get({id: $scope.report.id}).$promise.then(function(rep) {\n loading.resolve();\n report = rep;\n $scope.refresh_report();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Reload failed');\n });\n };\n \n $scope.refresh_report = function() {\n $scope.report = report;\n\n if (report.type == 'Matrix') {\n $scope.matrixDef = $scope.report.metadata.matrix;\n }\n else {\n $scope.matrixDef = {\n row: '',\n col: '',\n val: '',\n row_sort: 'alpha',\n col_sort: 'alpha',\n };\n }\n };\n $scope.refresh_report();\n\n $scope.canRemove = function(n) {\n if (n.status == 'started')\n return false;\n else if (userService.can({async_jobs: 'modify'}))\n return true;\n else if (userService.id == n.user_id)\n return true;\n else\n return false;\n };\n\n $scope.reload_runs = function() {\n loading.add();\n kwApi.Report.getRuns({id: $scope.report.id}).$promise.then(function(r) {\n loading.resolve();\n runs = r;\n $scope.refresh_runs();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Reload failed');\n });\n };\n\n $scope.refresh_runs = function() {\n var lst = angular.copy(runs);\n lst.sort(function(a,b) {\n var n = ($scope.order.reverse ? -1 : 1);\n if (a[$scope.order.field] < b[$scope.order.field]) {\n return -n;\n }\n if (a[$scope.order.field] > b[$scope.order.field]) {\n return n;\n }\n else {\n return 0;\n }\n });\n $scope.runs = lst;\n };\n\n $scope.$watch('order', function(newVal) {\n if (newVal) {\n $scope.refresh_runs();\n }\n }, true);\n\n $scope.refresh_runs();\n\n $scope.remove_run = function(n) {\n loading.add();\n kwApi.Report.deleteRun({id: report.id, rid: n.id}).$promise.then(function() {\n return kwApi.Report.getRuns({id: report.id}).$promise;\n }).then(function(r) {\n runs = r;\n $scope.refresh_runs();\n loading.resolve();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Remove failed');\n $scope.loading = false;\n });\n };\n\n $scope.$on('sys.signal.Report', function(evnt, args) { \n if (args.report_id == report.id)\n $scope.reload_runs();\n });\n \n $scope.validate_sql = function() {\n var valid = kwApi.Report.$validateSqlPattern($scope.report.savedsql);\n $scope.reportForm.sql_command.$invalid = !valid;\n }; \n}])\n\n.controller('StaffReportRunDetailsCtrl', [\"$scope\", \"report\", \"fullrun\", \"run\", \"kwApi\", \"loading\", \"alertService\", \"$sce\",\n function($scope, report, fullrun, run, kwApi, loading, alertService, $sce) {\n $scope.report = report;\n if (report.type_name == 'matrix') {\n $scope.matrixDef = angular.extend({\n row: \"(SQL SELECT column 1)\",\n col: \"(SQL SELECT column 2)\",\n val: \"(SQL SELECT column 3)\",\n }, $scope.report.metadata.matrix);\n }\n\n $scope.refresh = function(r) {\n $scope.run = r;\n $scope.data = r.return_value;\n if ($scope.data.is_tabular) {\n $scope.data.isHtml = [];\n for (var i=0; i<$scope.data.header.length; i++) {\n if ($scope.data.header[i].match(/_html$/)) {\n $scope.data.isHtml[i] = true;\n for (var j=0; j<$scope.data.rows.length; j++) {\n $sce.trustAsHtml($scope.data.rows[j][i]);\n }\n }\n else {\n $scope.data.isHtml[i] = false;\n }\n }\n }\n else {\n $scope.data.isHtml = false;\n if ($scope.data.val_field && $scope.data.val_field.match(/_html$/)) {\n $scope.data.isHtml = true;\n angular.forEach($scope.data.rows, function(row) {\n angular.forEach(row, function(col) {\n $sce.trustAsHtml(col);\n });\n });\n }\n }\n };\n\n $scope.reload = function() {\n loading.add();\n kwApi.Report.getRun({\n id: report.id,\n rid: run.id, \n sort: $scope.order.field,\n dir: ($scope.order.reverse ? 'DESC' : 'ASC'),\n start: $scope.start,\n count: $scope.count,\n }).$promise.then(function(r) {\n loading.resolve();\n $scope.refresh(r);\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err, \"Unable to load report run data\");\n });\n };\n\n if ($scope.isPrintView) {\n $scope.refresh(fullrun);\n }\n else {\n $scope.refresh(run);\n }\n\n $scope.has_biblionumber = ('biblio_column' in $scope.data) ? 1 : 0;\n $scope.has_itemnumber = ('item_column' in $scope.data) ? 1 : 0;\n\n $scope.start = 0;\n $scope.count = 20;\n $scope.pager = new KOHA.Pager({numResults: $scope.data.total_rows, offset: 0, numPerPage: $scope.count});\n $scope.toPage = function(page) {\n $scope.start = (page-1) * $scope.count;\n $scope.reload();\n };\n $scope.order = {\n field: '',\n reverse: false,\n };\n \n $scope.$watch('order', function(newVal) {\n if (newVal && newVal.field) {\n $scope.reload();\n }\n }, true);\n\n\n $scope.exportCsvLink = function(delim) {\n if (delim == 'tab') delim = \"\\t\";\n return kwApi.Report.$exportDownloadLink(report.id, run.id, {format: 'csv', delimiter: delim});\n };\n $scope.exportExcelLink = function() {\n return kwApi.Report.$exportDownloadLink(report.id, run.id, {format: 'xlsx'});\n };\n\n $scope.exportToBatch = function() {\n loading.add();\n kwApi.Report.exportRunAsBatch({id: report.id, rid: run.id}, {}).$promise.then(function(batch ) {\n loading.resolve();\n alertService.add({msg: \"Report exported to import batch \" + batch.id + \"\", type: \"info\"});\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Can\\'t export to import batch');\n });\n };\n $scope.exportToItemList= function() {\n loading.add();\n kwApi.Report.exportRunAsItemList({id: report.id, rid: run.id}, {}).$promise.then(function(batch ) {\n loading.resolve();\n alertService.add({msg: \"Report exported to item list \" + batch.id + \"\", type: \"info\"});\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Can\\'t export to item list');\n });\n };\n\n}])\n\n.controller('StaffReportNewCtrl', [\"$scope\", \"kwApi\", \"alertService\", \"loading\", \"$state\", function($scope, kwApi, alertService, loading, $state) {\n\n $scope.report = {\n type_name: 'Tabular',\n metadata: {},\n };\n\n $scope.report_types = [{\n label: 'Tabular', value: 'Tabular'\n }, {\n label: 'Matrix', value: 'Matrix'\n }];\n\n $scope.matrix_sort_types = [{\n label: 'Alphabetical', value: 'alpha'\n }, {\n label: 'Numeric', value: 'numeric'\n }];\n\n $scope.create_report = function() {\n if ($scope.report.type == 'Matrix') {\n $scope.report.metadata.matrix = $scope.matrixDef;\n }\n\n loading.add();\n kwApi.Report.save($scope.report).$promise.then(function(rep) {\n loading.resolve();\n alertService.add({msg: \"Report created\", type: \"info\"});\n $state.go('staff.reports.sql.report', {id: rep.id}, {reload: true});\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Report creation failed');\n });\n };\n \n $scope.validate_sql = function() {\n var valid = kwApi.Report.$validateSqlPattern($scope.report.savedsql);\n $scope.reportForm.sql_command.$invalid = !valid;\n }; \n}])\n\n\n.controller('StaffPrintMessagesCtrl', [\"$scope\", \"$http\", \"$uibModal\", function ($scope, $http, $uibModal) {\n $scope.numSelected = 0;\n\n $scope.viewDetails = function(s) {\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/tools/print-messages-detail.html',\n windowClass: 'modal',\n controller: [\"$scope\", function($scope) {\n $scope.rec = s[0];\n }],\n });\n };\n\n $scope.gridOptions = {\n enableSorting: true,\n enableColumnResizing: true,\n enableFiltering: true,\n minRowsToShow: 20,\n showGridFooter:true,\n enableFullRowSelection: true,\n rowTemplate: '
',\n columnDefs: [\n { field: 'id' },\n { field: 'firstname', displayName: 'First Name' },\n { field: 'surname', displayName: 'Last Name' },\n { field: 'cardnumber', displayName: 'Card Number' },\n { field: 'branchcode', displayName: 'Branch Code' },\n { field: 'title' },\n { field: 'code', displayName: 'Message Type' },\n { field: 'created_time', displayName: 'Created', cellFilter: 'dateFmt' },\n { field: 'content', visible: false },\n ]\n };\n\n $http.get('/api/message?op=get_fails', { authRequired: true }).then(function (rsp) {\n $scope.gridOptions.data = rsp.data;\n });\n\n $scope.gridOptions.onRegisterApi = function(gridApi) {\n $scope.myGridApi = gridApi;\n $scope.myGridApi.selection.on.rowSelectionChanged($scope, function(row) {\n if (row.isSelected) {\n $scope.numSelected++;\n }\n else {\n $scope.numSelected--;\n }\n });\n $scope.myGridApi.selection.on.rowSelectionChangedBatch($scope, function(rows) {\n rows.forEach(function(row) {\n if (row.isSelected) {\n $scope.numSelected++;\n }\n else {\n $scope.numSelected--;\n }\n });\n });\n };\n\n var getDocDefinition = function () {\n var gridSelection = $scope.myGridApi.selection.getSelectedRows();\n var marginsConfig = $scope.config.PrintMessagesMargins.split(/\\s*,\\s*/).map(Number);\n\n var isPositiveInteger = function (x) {\n return (typeof x === 'number') && (x % 1 === 0) && (x > 0);\n }\n\n for (var i = 0; i < 4; i++) { // Safety dance\n if ( !isPositiveInteger(marginsConfig[i]) ) marginsConfig[i] = 50;\n }\n\n if (marginsConfig.length > 4) marginsConfig = marginsConfig.slice(0, 4);\n\n var docDefinition = { content: [], pageMargins: marginsConfig };\n\n gridSelection.forEach(function(el) {\n if (el.content == null) {\n el.content = 'There was no content for ID# ' + el.id;\n }\n docDefinition.content.push( { text: el.content, fontSize: 12, pageBreak: 'after', preserveLeadingSpaces: true } );\n });\n\n delete docDefinition.content[docDefinition.content.length-1].pageBreak; // Delete blank last page.\n return docDefinition;\n }\n\n $scope.printMessages = function () {\n var docDefinition = getDocDefinition();\n pdfMake.createPdf(docDefinition).print();\n }\n\n $scope.downloadMessages = function () {\n var docDefinition = getDocDefinition();\n pdfMake.createPdf(docDefinition).download();\n }\n\n $scope.markPrinted = function () {\n var gridSelection = $scope.myGridApi.selection.getSelectedRows();\n\n gridSelection.forEach(function(el) {\n el.delivery_status = 'sent';\n $http.put('/api/message/' + el.id, JSON.stringify(el));\n });\n\n $scope.myGridApi.selection.clearSelectedRows();\n\n $http.get('/api/message?op=get_fails', { authRequired: true }).then(function (rsp) {\n $scope.gridOptions.data = rsp.data;\n });\n }\n\n}])\n\n.directive('uniqueSubfield', function() {\n return {\n require: 'ngModel',\n link: function(scope, elm, attrs, ctrl) {\n // FIXME: genericize.\n scope.$watchCollection(function(){ return scope.itemfields.map(function(f){ return f.subfield; }); },\n function(n,o){\n if(ctrl.$viewValue && jQuery.grep(n, function(v){ return v == ctrl.$viewValue ; }).length > 1){\n ctrl.$setValidity('unique', false);\n } else {\n ctrl.$setValidity('unique', true);\n }\n });\n }\n };\n})\n.directive('kwPatronSummary', [\"$stateParams\", function($stateParams) {\n return {\n templateUrl: '/app/static/partials/staff/patron-summary.html',\n scope: true,\n link: function(scope, elm, attrs, ctrl) {\n if(!$stateParams.borrowernumber){\n return;\n }\n // FIXME: This controller relies on parent scope's patron,\n // reloaded on state changes within the patron module (same patron).\n // We don't refresh on edit due to lack of appropriate signal.\n\n scope.imgurl = function(){\n return scope.patron.has_image ?\n '/bvcgi/members/patronimage.pl?crdnum=' + scope.patron.cardnumber\n : '/intranet-tmpl/prog/img/patron-blank.png';\n }\n }\n };\n}])\n\n.controller('StaffBibDetailCtrl', [\"$scope\", \"configService\", \"kwApi\", \"bibService\", \"$state\", function($scope, configService, kwApi, bibService, $state ) {\n\n var bibid = $state.params.biblionumber;\n bibService.get(bibid).then(function(bib){\n $scope.bib = bib;\n if(bib.isSerial){\n kwApi.Periodical.getForBib({bibid: bibid}, function(pubs){\n if(pubs.length) $scope.periodicals = pubs;\n });\n }\n });\n bibService.holdings(bibid, { cache: false, iface: 'staff' }).then(function(holdings){\n $scope.holdings = holdings;\n });\n $scope.config = configService;\n\n}])\n\n.controller('StaffMarcDisplayTmplCtrl', [\"$scope\", \"$http\", \"configService\", \"alertService\", \"kwApi\", \"kohaDlg\", \"$uibModal\", \"bibService\", \"marcBibSpec\", \"loading\", function($scope, $http, configService, alertService,\n kwApi, kohaDlg, $uibModal, bibService, marcBibSpec, loading) {\n\n var interfaces = {\n result: {\n desc: 'Search results',\n defaultTmpl: {}\n },\n detail: {\n desc: 'Bib detail (public)',\n defaultTmpl: {}\n },\n staffDetail: {\n desc: 'Staff detail',\n defaultTmpl: {}\n }\n };\n $scope.interfaces = interfaces;\n\n angular.forEach( interfaces, function( ifaceDef, iface ){\n console.log(iface);\n ifaceDef.activeID = configService.marcDisplayTmpl[iface].id;\n });\n console.log(interfaces);\n\n kwApi.MarcDisplayTmpl.getDefaults( {}, function(defaults){\n defaults.forEach(function(t){\n console.log(t);\n var iface = t.id.replace(/__/g, '');\n $scope.interfaces[iface].defaultTmpl = t;\n });\n });\n\n $scope.templates = kwApi.MarcDisplayTmpl.getAll();\n $scope.tmplNodeTmplUrl = '/app/static/partials/staff/admin/marcTmplNodeTmpl.html';\n\n $scope.isTmplActive = function(tmpl){ // ignores defaults.\n for( var iface in interfaces ){\n if(interfaces[iface].activeID == tmpl.id)\n return true;\n }\n return false;\n };\n $scope.dirtyActive = function(){\n for (var iface in interfaces){\n if(interfaces[iface].activeID){\n if(interfaces[iface].activeID != configService.marcDisplayTmpl[iface].id)\n return true;\n } else {\n console.warn('i dont think this is needed. theres always active id')\n if(configService.marcDisplayTmpl[iface].id != '__'+iface+'__')\n return true;\n }\n }\n return false;\n };\n $scope.saveActive = function(){\n var param = {};\n angular.forEach(interfaces, function(def, iface){\n param[iface] = /^__/.test(def.activeID) ? null : def.activeID;\n });\n $http.post('/api/marcdisplaytmpl?op=set_active', param).then(function(rsp){\n location.search = '_flush_cache'; //doesn't do anything - would need to reload.\n }, function(fail){\n console.warn(fail);\n loading.resolve();\n alertService.addApiError(fail);\n });\n }\n\n $scope.preview = {\n show: false,\n };\n\n $scope.newTmplDef = { id: \"\", clone: \"\" };\n $scope.createTmpl = function(tmplOpt){\n\n if( !tmplOpt.id || $scope.templates.find(function(tmpl){ return tmplOpt.id == tmpl.id; })){\n alertService.add({msg: 'Invalid template name .', type: 'error'});\n return;\n }\n\n var t = new kwApi.MarcDisplayTmpl({ id: tmplOpt.id, tmpl: [], isNew: true });\n if(tmplOpt.clone){\n var sourceTmpl;\n if(/^__/.test(tmplOpt.clone)){\n var iface= tmplOpt.clone.replace(/__/g, '');\n sourceTmpl = interfaces[iface].defaultTmpl;\n } else {\n sourceTmpl = $scope.templates.find(\n function(tmpl){ return tmplOpt.clone==tmpl.id; });\n }\n t.tmpl = angular.copy(sourceTmpl.tmpl);\n }\n $scope.openTmpl( t );\n $scope.newTmplDef.id = $scope.newTmplDef.clone = \"\";\n };\n\n $scope.openTmpl = function(tmplOrId){\n\n if(typeof tmplOrId == 'object')\n $scope.editing = tmplOrId;\n else if($scope.templates[tmplOrId])\n $scope.editing = $scope.templates[tmplOrId];\n }\n\n $scope.closeTmpl = function(){\n $scope.preview.show = false;\n $scope.editing = null;\n }\n\n $scope.saveTmpl = function(){\n\n var isNew = $scope.editing.isNew;\n $scope.editing.$save().then(function(ok){\n if(isNew)\n $scope.templates.push($scope.editing);\n $scope.editing = null;\n\n }).catch(function(e){\n console.warn(e);\n });\n\n }\n $scope.delTmpl = function( tmpl ){\n console.log(tmpl);\n if($scope.isTmplActive(tmpl))\n throw 'no bueno';\n\n kohaDlg.dialog({\n\n }).result.then(function(doit){\n if(doit){\n tmpl.$delete().then(function(){\n var i = $scope.templates.indexOf(tmpl);\n if(i>-1)\n $scope.templates.splice(i,1);\n }, function(e){\n console.log(e);\n alertService.addApiError(e, 'Could not delete template.');\n })\n }\n })\n\n }\n function collapseChildren (node) {\n node._isNodeCollapsed = true;\n if( node.childNodes ){\n node.childNodes.forEach(collapseChildren);\n }\n }\n $scope.collapseAll = function(){\n $scope.editing.tmpl.forEach(collapseChildren);\n }\n $scope.cloneDefault = function(iface){\n\n $scope.editing.tmpl = angular.copy($scope.interfaces[iface].defaultTmpl.tmpl);\n };\n $scope.marcLabeled = function(tagspec){\n // FIXME: Doesn't work for wildcards.\n return (tagspec||'').length > 2 ? marcBibSpec.hasMarcLabel( tagspec.substring(0,3) ) : false;\n }\n $scope.nodeTypeName = function(block){\n var nodeType = block.section ? 'Section' : (block.fields ? 'MARC field group' : 'Bib data');\n var labels = (block.label) ? [ block.label ]: [];\n if((block.fields||{}).length)\n labels.push( block.fields[0].tagspec + (block.fields.length>1 ? ' ...' : '') );\n return nodeType + ': ' + labels.join(' | ');\n\n }\n $scope.nodeLabel = function(node){\n\n }\n var defaultObj = {\n section: {\n section: true,\n label: \"\",\n className: \"\",\n childNodes: []\n },\n marc: {\n label: \"\",\n join: \"\",\n filterLast: \"\",\n fields: []\n },\n data: {\n label: \"\",\n className: \"\",\n join: \"\",\n data: \"\"\n },\n fieldSet: {\n tagSpec: \"\",\n label: \"\"\n },\n postProcess: {\n id: \"\",\n options: {}\n }\n }\n var select = {\n data: [\"language\", \"score\", \"format\"],\n postProcess: {\n wrapInner: {\n desc: \"Wraps the rendered fields in an html element.\",\n opt: {\n element: {\n desc: \"Element, e.g. 'h2'\",\n type: \"text\"\n }\n }\n },\n searchTrigger: {\n desc: \"Links to a catalog search via rcn or other search field.\",\n opt: {\n field: {\n desc: \"Index to search against if no rcn. (defaults to keyword/any)\",\n type: \"text\"\n }\n }\n },\n collapseFields: {\n desc: \"Hides some/all fields, adding a 'show more' link to reveal.\",\n opt: {\n trigger: {\n desc: \"Minimum number of fields to trigger collapse.\",\n type: \"number\"\n },\n show: {\n desc: \"Number of fields to show when collapsed.\",\n type: \"number\"\n }\n }\n },\n detailLink: {\n desc: \"This should be an option for wrapInner, i think.\",\n opt: {}\n },\n marc856: {\n desc: \"Adds 856 link functionality.\",\n opt: {}\n },\n linkedRecord: {\n desc: \"Adds links to catalog records linked by $w/$o [76X-78X].\",\n opt: {\n itemCount: {\n desc: \"Include count of items attached to the linked record.\",\n type: \"checkbox\"\n }\n }\n }\n }\n }\n $scope.newElTypes = {\n section: 'Section',\n marc: 'MARC Field Data',\n data: 'Non-field data'\n };\n $scope.selectOpts = select;\n $scope.sortableOptions = {\n connectWith: \".sortable-tree\",\n handle: \".drag-handle\"\n };\n $scope.defaultClassName = function(tmplNode){\n if(tmplNode.label)\n return tmplNode.label.toLowerCase().replace(/[^a-z ]+.*$/,'')\n .replace(/\\s+/g,'-') + '-data'; // from bvMarcDisplay directive.\n }\n $scope.toggleSectionCollapse = function(node, state){\n\n if(node.collapse)\n node.collapse = null;\n else\n node.collapse = { showText: '', hideText: ''};\n }\n $scope.addPostProcessor = function(id, node){\n node.push( { id: id, options: {} });\n }\n $scope.addEl = function(type, tmplTree, index){\n // adds after index to parent element.\n\n if(!defaultObj[type]){\n throw \"can't insert element.\";\n }\n var target = getParentNodeArray(tmplTree);\n\n if(!angular.isArray(target))\n throw \"Invalid parent node\";\n if(typeof index == 'undefined'){\n target.unshift(angular.copy(defaultObj[type]));\n } else {\n target.splice(index, 0, angular.copy(defaultObj[type]) )\n }\n\n\n };\n $scope.appendEl = function(type, obj){\n if(type in defaultObj)\n obj.push(angular.copy(defaultObj[type]));\n };\n $scope.rmEl = function( array, i ){\n array.splice(i,1);\n };\n\n function getParentNodeArray (tmplTree){\n // tmplTree is array of refs to parent nodes,\n // including current node.\n\n if(tmplTree.length < 2)\n throw \"can't find element.\";\n var target = tmplTree[ tmplTree.length-2 ];\n if(target.section)\n target = target.childNodes;\n return target;\n }\n $scope.rmTmplNode = function(node, tmplTree, i){\n\n if(typeof i == 'undefined' || tmplTree.length < 2){\n throw \"can't find element.\";\n }\n var target = getParentNodeArray(tmplTree);\n\n if(target[i] != node){\n console.log( angular.copy( target[i]));\n throw \"Bad traversal\";\n }\n target.splice(i,1);\n }\n\n\n}])\n.directive('marcDisplayPreviewPanel', [\"bibService\", \"$compile\", function(bibService, $compile){\n var wrapping = {\n detail: ['
','
'],\n staffDetail: ['
','
'],\n result: ['
','
',\n '
    ','
  1. ',\n '
    ', '
    ']\n };\n return {\n scope: {\n tmpl: '='\n },\n template:\n '
    ' +\n '
    ' +\n '
    ' +\n ' ' +\n ' ' +\n '
    ' +\n '
    Preview MARC Display| {{bib.title}}
    ' +\n '
    ' +\n '
    ' +\n '
    ' +\n '
    ' +\n '
    ',\n\n\n link: function(scope, el, attrs){\n\n scope.iface = 'result';\n scope.ifaceOptions = { detail: 'Public Detail',\n staffDetail: 'Staff Detail',\n staffResult: 'Search Result',\n result: 'Search Result (public)'};\n // FIXME: .staff is applied in parent no matter.\n var insertTarget = el.find('.bib-preview');\n var previewTmpl = '
    ';\n scope.render = function(){\n bibService.get( scope.bibid ).then(function(bib){\n scope.bib = bib;\n bibService.holdings( scope.bibid ).then(function(holdings){\n scope.preview = {\n solrbib: { score: 0.833 }, // fudge it.\n bib: bib,\n holdings: holdings\n };\n\n var $marcDisplay = $compile(previewTmpl)(scope);\n var isPublic = /^staff/.test(scope.iface);\n var wrapper = wrapping[scope.iface] || wrapping.result;\n\n var wrapped = wrapper.reduceRight(function(wrappedEl,html){\n return wrappedEl.wrap(html).parent();\n }, $marcDisplay);\n\n if(isPublic)\n wrapped.addClass('non-staff');\n insertTarget.html(wrapped);\n console.log(wrapped);\n })\n })\n }\n }\n }\n}])\n.controller('StaffAdminItemFieldsCtrl', [\"$scope\", \"$http\", \"configService\", \"alertService\", \"kohaDlg\", function($scope, $http, configService, alertService, kohaDlg) {\n // allow user to define custom item fields.\n // will be stored in items.fields as json .\n\n var xformType = function(fieldDef){\n if(fieldDef.typeAsString){\n if(fieldDef.typeAsString.substr(0,9)== \"authval__\"){\n fieldDef.authval = true;\n fieldDef.type = fieldDef.typeAsString.substr(9);\n } else {\n fieldDef.type = fieldDef.typeAsString;\n fieldDef.authval = false;\n }\n delete fieldDef.typeAsString;\n } else {\n if(fieldDef.authval){\n fieldDef.typeAsString = \"authval__\" + fieldDef.type;\n } else {\n fieldDef.typeAsString = fieldDef.type;\n }\n }\n return fieldDef;\n };\n $scope.updateType = function(fieldDef){\n if(fieldDef.typeAsString.substr(0,9)== \"authval__\"){\n fieldDef.authval = true;\n fieldDef.type = fieldDef.typeAsString.substr(9);\n } else {\n fieldDef.type = fieldDef.typeAsString;\n fieldDef.authval = false;\n }\n };\n\n $http.get('/api/syspref/ItemFields').then(function(rsp){\n configService.ItemFields = rsp.data.ItemFields;\n $scope.itemfields = angular.copy(configService.ItemFields).map(xformType);\n });\n\n $scope.cfg = configService;\n\n $scope.visibilityOptions = [\n { label: 'Results', val: 'results' },\n { label: 'Public', val: 'public' },\n { label: 'Staff', val: 'staff' },\n { label: 'Edit', val: 'edit' },\n { label: 'None', val: false }\n ];\n $scope.secondaryOptions = [\n { label: 'Yes', val: 'staff' },\n { label: 'Pub', val: 'public' },\n { label: 'No', val: false }\n ];\n // TODO: Provide type options from api.\n\n $scope.datatypes = [\n { label: \"Text\", typeAsString: \"string\", group: \"Data types\" },\n { label: \"Number\", typeAsString: \"int\", group: \"Data types\" },\n { label: \"Date\", typeAsString: \"date\", group: \"Data types\" },\n { label: \"Decimal\", typeAsString: \"decimal\", group: \"Data types\" },\n { label: \"Codabar\", typeAsString: \"codabar\", group: \"Data types\" },\n { label: \"Link\", typeAsString: \"uri\", group: \"Data types\" },\n { label: \"Availability\", typeAsString: \"_availability\", group: \"Derived data\" },\n ];\n\n configService.authvalList\n .map(function(av){ return { label: av, typeAsString: \"authval__\"+av, group: \"Authorised values\" }; })\n .forEach(function(def){ $scope.datatypes.push(def); });\n\n $scope.addRow = function(where){\n\n var newRow = { code: '', label: '', type: \"string\", custom: true,\n visibility: 'staff', editor: { } };\n if(where){\n $scope.itemfields.push(newRow);\n } else {\n $scope.itemfields.unshift(newRow);\n }\n };\n $scope.removeRow = function(i){\n kohaDlg.dialog({\n heading: 'Are you sure?',\n message: \"This action cannot be undone (once you save). Removing a custom field definition may cause custom data stored in item records to be inaccessible.\",\n }).result.then(function(rv) {\n if (rv) $scope.itemfields.splice(i,1);\n });\n };\n $scope.save = function(){\n\n if($scope.itemfieldsform.$invalid){\n kohaDlg.dialog({\n type: \"notify\",\n alertClass: \"warning\",\n heading: \"Invalid fields\",\n message: \"Item fields must have non-empty and unique codes.\"\n });\n return;\n }\n var toSend = { ItemFields: angular.copy($scope.itemfields).map(xformType) };\n $http.put('/api/syspref/ItemFields', JSON.stringify(toSend))\n .then(function(r){\n $scope.itemfieldsform.$setPristine();\n alertService.add({msg: \"Changes successfully saved\", type: \"success\"});\n $http.get('/api/syspref/ItemFields').then(function(rsp){\n console.dir(rsp);\n configService.ItemFields = rsp.data.ItemFields;\n $scope.itemfields = angular.copy(configService.ItemFields).map(xformType);\n });\n }, function(err){\n alertService.addApiError(err,'Error saving data');\n });\n };\n\n $scope.resetDefault = function(){\n var message = \"This action cannot be undone. \";\n message += 'Custom item field data will be inaccessible if their definitions are removed.';\n kohaDlg.dialog({\n heading: 'Are you sure?',\n message: message,\n // buttons: [{val: true, label: 'Update', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}]\n }).result.then(function(rv) {\n if (rv) {\n $http.post('/api/syspref/ItemFields', $.param({ op: 'reset' }),{headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}})\n .then(function(rsp){\n console.log(rsp);\n $scope.itemfieldsform.$setPristine();\n alertService.add({msg: \"Default values restored.\", type: \"success\"});\n configService.ItemFields = rsp.data.ItemFields;\n $scope.itemfields = angular.copy(configService.ItemFields).map(xformType);\n }, function(e){\n alertService.addApiError(e,'Itemfields data failed to reset');\n console.warn(e);\n });\n }\n });\n\n };\n\n}])\n\n.controller('StaffItemMigrateCtrl', [\"$scope\", \"$stateParams\", \"alertService\", \"bibService\", \"$http\", function( $scope, $stateParams, alertService, bibService, $http ) {\n\n bibService.get( $stateParams.biblionumber ).then( function( bib ) {\n $scope.bib = bib;\n });\n\n $scope.migrateItem = function ( barcode ) {\n if ( ! barcode ) {\n return;\n }\n else {\n $http.post( '/api/work/' + $scope.bib.id + '?op=migrate-item&barcode=' + barcode, { authRequired: true } ).then( function( rsp ) {\n alertService.add( { type: 'success', msg: 'The item was migrated successfully' } );\n $scope.barcode = undefined;\n }, function( err ) {\n console.error( err );\n alertService.addApiError(err,'Save failed');\n });\n }\n };\n}])\n\n.controller('StaffItemsCtrl', [\"$scope\", \"$uibModal\", \"$stateParams\", \"$state\", \"$q\", \"configService\", \"bibService\", \"alertService\", \"kohaDlg\", \"$http\", \"loading\", function($scope, $uibModal, $stateParams, $state, $q, /*holdings,*/\n configService, bibService, alertService, kohaDlg, $http, loading ) {\n\n var self = this;\n\n// item edit interface.\n// holdings table with modal item editor.\n\n bibService.get($stateParams.biblionumber).then(function(bib){\n $scope.bib = bib;\n });\n\n var holdingsP = bibService.holdings($stateParams.biblionumber, { cache: false }).then(function(holdings){\n $scope.holdings = holdings;\n return holdings;\n });\n\n $scope.iface = 'edit';\n $scope.itemGroups = {\n toDelete: {},\n deleteCount: function(){ return Object.keys(this.toDelete).length ;},\n clear: function(){\n $scope.holdings.clearModState();\n this.toDelete = {};\n }\n };\n\n $scope.mfhdAsStr = function(mfhd){\n return mfhd.id + ': ' + mfhd.location.homebranch + '| ' +\n mfhd.location.location + '| ' + mfhd.location.itemcallnumber;\n };\n\n $scope.deleteItems = function( itemids ){\n var holdings = $scope.holdings;\n var failed = {};\n var promises = [];\n if( ! itemids ) itemids = Object.keys($scope.itemGroups.toDelete);\n var itemDesc = (itemids.length>1) ? (itemids.length + \" items \") :\n ( \" item \" + holdings.item[itemids[0]].id +\n \" [\" + holdings.item[itemids[0]].barcode + \"]\");\n\n var firstConfirm = kohaDlg.dialog({\n message: \"Confirm deletion of \" + itemDesc + '. ' +\n 'This action cannot be undone.',\n buttons: [ { label: 'Delete !', val: true, btnClass: 'btn-danger'},\n {label: 'Cancel', val: false, btnClass: 'btn-outline-secondary'} ],\n });\n return firstConfirm.result.then(function(ok){\n if(!ok){\n $scope.itemGroups.toDelete = {};\n $scope.holdings.clearModState();\n return null;\n }\n\n itemids.forEach(function(itemid){\n var deferred = $q.defer();\n promises.push(deferred.promise);\n holdings.item[itemid].$delete().then(function(rsp){\n alertService.add({ type: 'success', msg: 'ITEM successfully deleted.'});\n holdings.rmItem(itemid);\n deferred.resolve();\n }, function(e){\n if(e.status=='409'){\n failed[itemid] = e.data;\n } else {\n alertService.add({ type: 'error', msg: 'Item delete failed: ' + e.data });\n }\n deferred.resolve();\n });\n\n });\n\n return $q.all(promises).then(function(){\n\n if(Object.keys(failed).length){\n var reallyDelete = $uibModal.open({\n backdrop: false,\n templateUrl: '/app/static/partials/staff/item-delete-confirm.html',\n controller: 'StaffItemDeleteModalCtrl',\n resolve: {\n holdings: function() { return $scope.holdings; },\n items: function() { return failed; }\n }\n });\n return reallyDelete.result.then(function(confirmed){\n if(confirmed){\n var retries = [];\n angular.forEach(failed, function(failedConstraints, itemid){\n var params = {};\n Object.keys(failedConstraints).forEach(function(check){\n params[check] = 0;\n });\n var item = holdings.item[itemid];\n var retry = $q.defer();\n retries.push(retry.promise);\n item.$delete(params).then(function(rsp){\n $scope.holdings.rmItem(itemid);\n alertService.add({ type: 'success', msg: 'Item ' + itemid + ' successfully deleted.'});\n }, function(e){\n item._saveStatus = 'deleteFailed';\n alertService.addApiError(e,'Item cannot be deleted');\n }).finally(function(itm){\n retry.resolve(itm);\n });\n });\n return $q.all(retries);\n }\n });\n }\n }).finally(function(){\n $scope.itemGroups.toDelete = {};\n\n // FIXME: if this is last of a dummy mfhd, it's left hanging.\n });\n });\n };\n\n $scope.mvItems = function(target_id, src){\n // if item_id is null, move all items in mfhd.\n // if target_id is null, unlink them.\n if(!src) return;\n var mfhd = src.mfhd;\n var item_id = src.item_id;\n\n if(mfhd && item_id && !mfhd._dummy && $scope.holdings.item[item_id].mfhd_id != mfhd.id){\n console.warn(\"MFHD ID MISMATCH\");\n return;\n }\n loading.add('mfhd.link');\n var itemsToMove = ( item_id ) ? [ $scope.holdings.item[item_id] ] : mfhd.items;\n var moved = itemsToMove.map(function(item){\n return item.$relinkMfhd({ target_mfhd_id: target_id });\n });\n $q.all(moved).then(function(all){\n $scope.holdings.refresh().then(function(h){\n loading.resolve('mfhd.link');\n itemsToMove.forEach(function(item){\n h.item[item.id]._saveStatus = 'modified';\n });\n });\n }, function(f){\n loading.resolve('mfhd.link');\n console.warn(f);\n });\n };\n\n\n $scope.openEditModal = function(target){\n if(!target) target = {};\n var openEditor = function() {\n if(target.item){ // will clear on save.\n $scope.holdings.clearModState();\n $scope.holdings.item[target.item]._saveStatus = 'editing';\n }\n return $uibModal.open({\n backdrop: false,\n templateUrl: '/app/static/partials/staff/item-edit-modal.html',\n controller: 'StaffItemEditCtrl',\n resolve: {\n holdings: function() { return $scope.holdings; },\n itemid: function() { return target.item; },\n mfhdid: function(){ return target.mfhd; },\n clone: function(){ return target.clone; }\n }\n }).result.then( function(edited){\n if(edited){\n edited.id = target.item;\n return afterEdits(edited);\n } else {\n $scope.holdings.clearModState();\n }\n\n });\n };\n var permissionFail = function(e) {\n alertService.add({msg: \"You do not have permission to edit records for this branch.\", type: \"error\"})\n console.error(e.statusText);\n };\n\n if (target.item) {\n return $http.head('/api/item/' + target.item).then(openEditor, permissionFail);\n } else {\n return openEditor();\n }\n\n };\n\n function afterEdits(edited) {\n\n //editor modal returns {\n // id: original item id if not 'add'.\n // item: itemresource,\n // multiAdd: array of extra created items,\n // duplicate: another modal result. }\n\n var maybeRefresh = $q.when(false);\n\n // if(edited.multi) maybeRefresh = $scope.holdings.refresh();\n if(edited.id){\n // FIXME:\n // if user modifies home branch,\n // need to move mfhds or trigger refresh.\n // ...handle this in editor ctrl.\n\n }\n\n maybeRefresh.then(function(){\n $scope.holdings.clearModState(); //no need if refreshed.\n if(edited.id){\n $scope.holdings.item[edited.item.id]._saveStatus = 'modified';\n } else {\n $scope.holdings.item[edited.item.id]._saveStatus = 'added';\n }\n\n if(edited.duplicate)\n edited.duplicate.then(afterEdits);\n if(edited.multi){\n edited.multi.forEach(function(item){\n $scope.holdings.item[item.id]._saveStatus = 'added';\n });\n }\n })\n }\n if($stateParams.edit ){\n var seen = {};\n var arr = (angular.isArray($stateParams.edit)) ?\n $stateParams.edit : [ $stateParams.edit ];\n\n arr.forEach(function(itemid){\n if(!itemid) itemid = 0;\n if(!seen[itemid])\n holdingsP.then(function(){\n $scope.openEditModal({ item: parseInt(itemid) }).then(function(){\n $state.go( '.', { edit: null }, { notify: false } );\n });\n });\n seen[itemid] = true;\n });\n }\n\n}])\n\n.controller('StaffAdminItemStatusConfigCtrl', [\"$scope\", \"kwApi\", \"alertService\", function($scope, kwApi, alertService){\n $scope.itemstatuses = kwApi.ItemStatus.getAll();\n\n $scope.addRow = function(status){\n $scope.itemstatuses.push( new kwApi.ItemStatus() );\n };\n $scope.saveRow = function(status){\n console.log(status);\n\n if(status.id){\n status.$update({id: status.id}).then(function(j){\n alertService.add({msg: \"Item Status updated.\", type: \"success\"})\n }, function(e){\n alertService.addApiError(e,'Item status update failed');\n console.warn(e.data);\n });\n } else {\n status.$save({ op: 'create' }).then(function () {\n alertService.add({msg: \"Item Status created.\", type: \"success\"})\n }, function (e) {\n alertService.addApiError(e,'Item status creation failed');\n console.warn(e.data);\n });\n }\n };\n $scope.deleteRow = function(status, i){\n console.log(i);\n var remove = function(i){\n $scope.itemstatuses.splice(i,1);\n };\n if(status.id){\n status.$delete({id: status.id}, function(rv){\n alertService.add({msg: \"Item Status deleted.\", type: \"success\"});\n remove(i); // todo animate\n }, function(e){\n alertService.add({msg: \"Item Status delete failed: \" + e.data.replace(/\\d\\d\\d/, '')\n .replace('Item status', ''), type: \"error\"});\n console.warn(e.data);\n });\n } else {\n remove(i);\n }\n };\n}])\n.controller('StaffItemsCircStatusCtrl', [\"$scope\", \"$http\", \"$stateParams\", \"$q\", \"$uibModal\", \"$filter\", \"$state\", \"kwApi\", \"kohaDlg\", \"configService\", \"bibService\", \"userService\", \"alertService\", \"$timeout\", \"bvItemSvc\", \"$window\", \"Pager\", \"$anchorScroll\", function($scope, $http,$stateParams,$q, $uibModal, $filter,\n $state, kwApi, kohaDlg, configService, bibService, userService, alertService, $timeout, bvItemSvc, $window, Pager, $anchorScroll) {\n\n if(!$stateParams.biblionumber) $state.go('staff');\n bibService.get($stateParams.biblionumber).then(function(bib){\n $scope.bib = bib;\n });\n $scope.collapsed = {};\n $scope.display = configService.display;\n $scope.tmp = {};\n $scope.pager = {};\n $scope.pageData = [];\n var pagelength = 10;\n var oneTime = false;\n\n $scope.pageChanged = function(){\n if($stateParams.itemid && !oneTime){\n oneTime = true;\n var itemIndex = $scope.items.findIndex( function(item){ return item.id == $stateParams.itemid } );\n $scope.pager.page = Math.ceil((itemIndex+1)/pagelength);\n $scope.pageData = $scope.items.slice($scope.pager.offset(), $scope.pager.rangeEnd());\n }\n else if ($scope.items.length <= pagelength){\n $scope.pageData = $scope.items;\n }\n else {\n $scope.pageData = $scope.items.slice($scope.pager.offset(), $scope.pager.rangeEnd());\n }\n\n $scope.pageData.forEach(function(item){\n if(!$scope.tmp[item.id].issues){\n updateIssues(item);\n $scope.tmp[item.id].showIssuedata = true;\n }\n });\n };\n\n $scope.onlyShow = $stateParams.itemid;\n $scope.showAll = function() {\n $scope.onlyShow = false;\n $scope.collapsed = {};\n $timeout(function(){\n $anchorScroll('item-'+$stateParams.itemid);\n },200);\n };\n $scope.loadIssuedata = function(item){\n if( ! $scope.tmp[item.id].issues)\n updateIssues(item);\n };\n $scope.canUserEdit = userService.can({editcatalogue: { item: 'edit' }});\n $scope.canUserModStatus = function(branchcode) {\n return userService.canInBranch({editcatalogue: { item: 'status' }}, branchcode);\n };\n\n $scope.printSpineLabel = function (item) {\n $window.open(\"/app/staff/labels/spine-create?item=\" + item.itemnumber);\n };\n var updateIssues = function(item){\n var stash = $scope.tmp[item.id];\n stash.issues = kwApi.Issue.itemIssues({id: item.id}, function(issues){\n // if(!issues.length) stash.neverBorrowed = true;\n // stash.renewalCount = issues.reduce(function(acc,cv){ return acc + cv.renewals||0; }, 0);\n var thisYear = dayjs().year();\n stash.circ = {\n issues: { total: issues.length, ytd: 0},\n renewals: { total: 0, ytd: 0},\n lastIssue: issues[0]\n };\n issues.forEach(function(issue){\n stash.circ.renewals.total += Number(issue.renewals)||0;\n if(dayjs(issue.issuedate).year()==thisYear){\n stash.circ.issues.ytd++;\n stash.circ.renewals.ytd += Number(issue.renewals)||0;\n }\n });\n\n });\n };\n var updateLostItem = function(item){\n var stash = $scope.tmp[item.id];\n var lostitem = kwApi.LostItem.get({ item_id: item.id }, function(){\n stash.lostitem = lostitem;\n lostitem.loserName = lostitem.borrowernumber;\n kwApi.Patron.get({id: lostitem.borrowernumber}, function(patron){\n lostitem.loserName = patron.surname + ', ' + patron.firstname;\n // TODO: make a directive with cached name.\n });\n }, function(f){\n stash.lostitem = null;\n });\n };\n var stashStatuses = function(item){\n if(!$scope.tmp[item.id]) $scope.tmp[item.id] = {};\n $scope.tmp[item.id].wthdrawn = item.wthdrawn;\n $scope.tmp[item.id].damaged = item.damaged;\n $scope.tmp[item.id].itemlost = item.itemlost||'';\n $scope.tmp[item.id].itemnotes = item.itemnotes;\n };\n kwApi.Item.workItems( { id: $stateParams.biblionumber }, function(items){\n $scope.pager = new Pager({ count: items.length, pagelength: pagelength });\n $scope.items = bvItemSvc.sort(items, '_sorthomebranch');\n $scope.items.forEach(function(item){\n stashStatuses(item);\n\n // If lost, find borrower.\n if(item.itemlost){\n updateLostItem(item);\n }\n var onlyItemShown = $stateParams.itemid == item.id;\n\n if($stateParams.itemid)\n $scope.collapsed[item.id] = !onlyItemShown;\n\n bvItemSvc.itemIsAvailable(item);\n });\n\n $scope.pageChanged();\n });\n $scope.date = $filter('kohaDate');\n\n $scope.updatingLost = function(item){\n if(!$scope.tmp[item.id].itemlost && !item.itemlost) return false;\n return ( $scope.tmp[item.id].itemlost != item.itemlost);\n };\n $scope.options = { damaged: configService.interpolator('damaged').options(),\n itemlost: configService.interpolator('lost').options(),\n withdrawn: configService.interpolator('withdrawn').options() };\n\n $scope.updateCircStatus = function(item, status){\n var param = { op: 'set_status' };\n param[status] = $scope.tmp[item.id][status];\n if(status=='itemlost' && ! param[status]) param[status] = null; // dynamic types ftw.\n var lost_item = $scope.tmp[item.id].lostitem;\n var maybeWarn = $q.when(true);\n if(status=='itemlost' && param.itemlost=='lost'){\n var dlg = {'message': 'Patron will be charged for lost item. Continue?'};\n maybeWarn = kohaDlg.dialog(dlg).result;\n } else if(status=='itemlost' && param.itemlost!='lost' && item.itemlost=='lost'){\n var lost_handled = $q.defer();\n maybeWarn = lost_handled.promise;\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/circ/lost-item-modal.html',\n animation: false,\n keyboard: false,\n backdrop: 'static',\n controller: [\"$scope\", function($scope) {\n\n // Note this tmpl is used in checkin as well, duplicate controller logic.\n $scope.context = 'lost_status';\n\n $scope.canHandleLostItem = userService.can({borrowers: 'delete_lost_items'});\n $scope.canRefundLostItem = configService.RefundReturnedLostItem;\n $scope.lost = {\n remove: !!$scope.canHandleLostItem,\n refund: ($scope.canRefundLostItem) ? 'LOSTRETURNED' : ''\n };\n\n if(lost_item){\n kwApi.Patron.get({id: lost_item.borrowernumber}, function(patron){\n $scope.loser = patron;\n $scope.loserName = patron.surname + ', ' + patron.firstname;\n });\n $scope.loserName = lost_item.borrowernumber;\n } else {\n $scope.noLostItemRecord = true;\n }\n\n $scope.handleLost = function() {\n\n if( $scope.noLostItemRecord ){\n lost_handled.resolve(true);\n } else if( ! $scope.canHandleLostItem ){\n lost_item.$defer( { id: lost_item.id } ).then(\n function(){\n lost_handled.resolve(true);\n });\n\n } else {\n lost_item.$checkin( {\n id: lost_item.id,\n remove: $scope.lost.remove,\n refund: $scope.lost.refund\n }).then(function success(rsp){\n lost_handled.resolve(true);\n }, function err(e){\n alertService.addApiError(e,'Failed to handle lost item');\n lost_handled.reject();\n });\n }\n\n $scope.$close();\n };\n $scope.cancel = function(){\n lost_handled.resolve(false);\n $scope.$close();\n };\n }],\n });\n }\n\n // $scope.tmp[item.id].showIssuedata = false; // triggers reload in view via ngIf.\n maybeWarn.then(function(ok){\n if(ok){\n $http.post('/api/item/'+item.id, param).then(function(rsp){\n stashStatuses(rsp.data);\n // This is flaky -- should use $resource and get new item instance.\n item[status] = rsp.data[status];\n item.onloan = rsp.data.onloan;\n item.statuses = rsp.data.statuses;\n item.$is_available = bvItemSvc.itemIsAvailable(item);\n item.timestamp = rsp.data.timestamp; // watched by availability directiv.\n updateLostItem(item);\n }, function(f){\n console.warn(f);\n var oldstatus = (status=='itemlost') ? item[status] || '' : item[status];\n $scope.tmp[item.id][status] = oldstatus;\n if(f.data=='400 No patron to assign.'){\n alertService.add({\n msg: 'No patron found to assign lost item. [invalid lost status].',\n type: 'error',\n persist: true });\n }\n }).finally(function testHoldsOnItemMod(){\n var itemlvl = false;\n var callModal = false;\n\n kwApi.Work.get( { id: $scope.bib.id }, function(work){\n // $scope.tmp[item.id].showIssuedata = true;\n if (work.summary.holdable_count == 0 && work.summary.holds_count > 0) {\n callModal = true;\n }\n else if (item.on_hold && ( // This is written out since `item.circulates` is not reliable.\n (item.damaged > 0 && !configService.AllowHoldsOnDamagedItems)\n || item.wthdrawn > 0 || item.notforloan > 0 || item.itemlost)\n ){\n callModal = true;\n itemlvl = true;\n }\n\n if (callModal) {\n $uibModal.open({\n backdrop: false,\n templateUrl: '/app/static/partials/staff/holds-cancel-modal.html',\n controller: 'CancelBibHoldsModalCtrl',\n resolve: {\n itemlvl: function() { return itemlvl; },\n bibid: function() { return $stateParams.biblionumber; },\n itemid: function() { return item.id; },\n }\n });\n }\n });\n });\n } else {\n $scope.tmp[item.id][status] = item[status];\n }\n });\n };\n\n $scope.itemstatuses = configService.itemstatuses;\n $scope.addStatus = function(item){\n if(!$scope.tmp[item.id].newstatus) return;\n if( item.statuses.filter(function(stat){\n return $scope.tmp[item.id].newstatus==stat.id; }).length ){\n $scope.tmp[item.id].newstatus = null;//dupe\n return;\n }\n $scope.tmp[item.id].showIssuedata = false; // triggers reload in view via ngIf.\n item.$setStatus({statusid: $scope.tmp[item.id].newstatus}).then(function(){\n $scope.tmp[item.id].newstatus = null;\n $scope.tmp[item.id].showIssuedata = true;\n }, function(e){\n console.warn(e);\n });\n };\n $scope.rmStatus = function(item, status){\n $scope.tmp[item.id].showIssuedata = false;\n item.$clearStatus({statusid: status.id}).then(function(){\n $scope.tmp[item.id].showIssuedata = true;\n });\n };\n\n}])\n\n.controller('StaffItemDeleteModalCtrl', [\"$scope\", \"items\", function($scope, items){\n\n // injected items : itemid as key and response from item.$delete as val.\n\n $scope.items = items;\n var re = /_/g;\n $scope.failureDisplay = function(failure){\n return \"Item is \" + failure.replace(re,' ');\n };\n\n}])\n\n.controller('CancelBibHoldsModalCtrl', [\"$scope\", \"$uibModalInstance\", \"$http\", \"alertService\", \"itemlvl\", \"bibid\", \"itemid\", function($scope, $uibModalInstance, $http, alertService, itemlvl, bibid, itemid){\n $scope.itemlvl = itemlvl;\n $scope.bibid = bibid;\n $scope.itemid = itemid;\n\n $scope.cancelItemHolds = function (bibid, itemid) {\n var myHeaders = {'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'};\n $http.post('/api/holds-queue/' + bibid, $.param({op: 'cancel-item-holds', itemid: itemid}), {headers: myHeaders, authRequired: true})\n .then( function () {\n alertService.add({msg: 'All holds for the item have been canceled.', type: 'success'});\n }, function (e) {\n alertService.addApiError(e,'Failed to cancel holds');\n });\n $uibModalInstance.close();\n };\n\n $scope.cancelAllHolds = function (bibid) {\n var myHeaders = {'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'};\n $http.post('/api/holds-queue/' + bibid, $.param({op: 'cancel-all'}), {headers: myHeaders, authRequired: true})\n .then( function () {\n alertService.add({msg: 'All holds have been canceled.', type: 'success'});\n }, function (e) {\n alertService.addApiError(e,'Failed to cancel holds');\n });\n $uibModalInstance.close();\n };\n\n}])\n\n.controller('StaffItemEditCtrl', [\"$scope\", \"$uibModalInstance\", \"$uibModal\", \"$q\", \"kwApi\", \"configService\", \"userService\", \"alertService\", \"holdings\", \"itemid\", \"mfhdid\", \"clone\", \"$http\", \"kohaDlg\", \"bvItemSvc\", function($scope, $uibModalInstance,\n $uibModal, $q, kwApi, configService, userService, alertService, holdings,\n itemid, mfhdid, clone, $http, kohaDlg, bvItemSvc){\n\n // edit modal controller.\n if(itemid && mfhdid) console.warn(\"Cannot move item to new MFHD while editing.\");\n\n $scope.saveOptions = {\n addMulti: 1,\n dupe: false\n };\n $scope.vis = {\n optionsDrop: false\n };\n\n $scope.toggleDupe = function(){\n $scope.saveOptions.dupe = !$scope.saveOptions.dupe;\n $scope.saveOptions.addMulti = 1;\n $scope.vis.optionsDrop = false;\n };\n\n $scope.holdings = holdings;\n\n var cloneItem = function(item){\n if(typeof item != \"object\") item = holdings.item[item];\n if(!item) return;\n var newItem = angular.copy(item);\n delete newItem.id;\n delete newItem.itemnumber;\n delete newItem.uuid;\n delete newItem.itemlost;\n delete newItem.damaged;\n delete newItem.wthdrawn;\n if(newItem.barcode && +newItem.barcode < 9007199254740991){ // 9007199254740991 = Number.MAX_SAFE_INTEGER (ES6)\n var nextBarcode = +newItem.barcode + 1;\n newItem.barcode = isNaN(nextBarcode) ? undefined : nextBarcode.toString();\n } else {\n newItem.barcode = undefined;\n }\n return newItem;\n };\n\n var target_mfhd_id;\n\n var newItemDefaults = function(){\n var bib_cn;\n if(/^\\d{3}\\w+$/.test(configService.itemcallnumber)){\n var field = holdings.bib.marc.field(configService.itemcallnumber.substr(0,3));\n if(field){\n bib_cn = configService.itemcallnumber.substr(3).split('').map(\n function(sfcode){ return field.subfield(sfcode); }).filter(\n function(val){ return val; }).join(' ');\n }\n }\n var defaults = {\n biblionumber: holdings.bib.id,\n homebranch: userService.login_branch,\n holdingbranch: userService.login_branch,\n itemcallnumber: bib_cn\n };\n\n configService.ItemFields.forEach(function(fieldDef){\n if(fieldDef.editor.default) defaults[fieldDef.code] = fieldDef.editor.default;\n });\n return defaults;\n };\n\n if(itemid){\n\n $scope.item = angular.copy(holdings.item[itemid]);\n } else {\n if(clone){\n $scope.item = cloneItem(clone);\n target_mfhd_id = mfhdid || $scope.item.mfhd_id;\n\n } else {\n $scope.item = new kwApi.Item();\n $scope.item.$applyDefaults(newItemDefaults());\n\n if(mfhdid){\n $scope.item.holdingbranch = $scope.item.homebranch = holdings.mfhd[mfhdid].location.homebranch;\n if(!holdings.mfhd[mfhdid]._dummy){\n ['location','ccode','itemcallnumber'].forEach(function(field){\n if(holdings.mfhd[mfhdid].location[field])\n $scope.item[field] = holdings.mfhd[mfhdid].location[field];\n });\n target_mfhd_id = +mfhdid;\n }\n }\n }\n }\n\n var confVisibleFields = configService.ItemFields.filter(function(fieldDef){ return fieldDef.visibility; });\n var userHiddenFields = userService.getPref('staff.item_edit.hidden_fields');\n\n userHiddenFields ? $scope.userHidden = userHiddenFields : $scope.userHidden = [];\n\n if ($scope.userHidden && $scope.userHidden.length > 0) {\n $scope.showHideBtn = false;\n $scope.visibleFields = confVisibleFields.filter( function(f){ return $scope.userHidden.indexOf(f.code) < 0; });\n }\n else {\n $scope.showHideBtn = true;\n $scope.visibleFields = confVisibleFields;\n }\n\n $scope.resetHidden = function(){\n $scope.showSaveHideBtn = false;\n $scope.userHidden = [];\n $scope.visibleFields = confVisibleFields;\n $scope.saveHidden();\n };\n\n $scope.selectHidden = function(){\n $scope.showHideBtn = false;\n $scope.showSaveHideBtn = true;\n };\n\n $scope.hideField = function(f){\n $scope.userHidden.push(f);\n $scope.visibleFields = confVisibleFields.filter( function(f){ return $scope.userHidden.indexOf(f.code) < 0; });\n };\n\n $scope.saveHidden = function(){\n userService.setPref('staff.item_edit.hidden_fields', $scope.userHidden);\n alertService.add({msg: \"Edit item field visibility preference successfully updated.\", type: \"success\" });\n ($scope.userHidden && $scope.userHidden.length == 0) ? $scope.showHideBtn = true : $scope.showHideBtn = false;\n $scope.showSaveHideBtn = false;\n };\n\n var editorRV = {\n item: $scope.item,\n id: itemid,\n doNotClose: false\n };\n\n $scope.save = function(item){\n if(!item) item = $scope.item;\n var requestedBarcode = item.barcode;\n\n if(item.id){\n // copy edited item's values back to holdings\n // to retain holdings object's multiple refs to item.\n item = holdings.item[item.id].$copyFrom( item );\n editorRV.item = item;\n item.$put().then(function(editedItem){\n\n holdings.modified( editedItem.timestamp);\n\n alertService.add({msg: \"Item successfully updated.\", type: \"success\" });\n editorRV.doNotClose = false;\n if(requestedBarcode && item.barcode != requestedBarcode){\n alertService.add({msg: \"Requested Barcode unavailable: \" + requestedBarcode});\n }\n\n $scope.itemlvl = false;\n var callModal = false;\n\n $http.get('/api/work/' + $scope.holdings.bib.id).then(function (r) {\n if (r.data.summary.holdable_count === 0 && r.data.summary.holds_count > 0) {\n callModal = true;\n }\n else if (holdings.item && holdings.item[item.id].on_hold && !holdings.item[item.id].circulates){\n callModal = true;\n $scope.itemlvl = true;\n }\n\n if (callModal) {\n $uibModal.open({\n backdrop: false,\n templateUrl: '/app/static/partials/staff/holds-cancel-modal.html',\n controller: 'CancelBibHoldsModalCtrl',\n resolve: {\n itemlvl: function() { return $scope.itemlvl; },\n bibid: function() { return $scope.holdings.bib.id },\n itemid: function() { return item.id; },\n }\n });\n }\n });\n return item;\n }, function(e){\n console.warn('Ensure caller reloads holdings.');\n // or maybe just do it here.\n saveError(e);\n editorRV.error = true;\n editorRV.doNotClose = true;\n return item;\n }).then(function(item){\n if (!editorRV.doNotClose) $uibModalInstance.close(editorRV);\n });\n } else {\n\n var createItem = item.$create().then(function(newItem){\n\n alertService.add({msg: \"Item successfully added\", type: \"success\" });\n editorRV.doNotClose = false;\n if(requestedBarcode && item.barcode != requestedBarcode){\n alertService.add({msg: \"Requested Barcode unavailable: \" + requestedBarcode});\n }\n\n if(target_mfhd_id){\n return newItem.$relinkMfhd( {target_mfhd_id: target_mfhd_id} ).\n then(function(){\n newItem.mfhd_id = target_mfhd_id; // ideally should happen in $relinkMfhd.\n return newItem;\n }).\n catch(function(err){\n // this should trigger full refresh.\n console.warn(err);\n alertService.addApiError(err,'Item could not be linked to holdings record');\n });\n }\n return newItem;\n\n }).catch(function(e){\n saveError(e);\n editorRV.error = true;\n\n return $q.reject(e);\n });\n\n createItem.then(function(newItem){\n if($scope.saveOptions.addMulti <= 1){\n holdings.addItem( newItem );\n }\n console.log(holdings);\n if($scope.saveOptions.dupe){\n var addAnotherModal = $uibModal.open({\n backdrop: false,\n templateUrl: '/app/static/partials/staff/item-edit-modal.html',\n controller: 'StaffItemEditCtrl',\n resolve: {\n holdings: function() { return holdings; },\n itemid: function() { return null; },\n mfhdid: function(){ return mfhdid; },\n clone: function(){ return angular.copy(item); }\n }\n });\n\n // in each case, might return a nested modal.\n editorRV.duplicate = addAnotherModal.result;\n return editorRV;\n\n } else if($scope.saveOptions.addMulti > 1){\n\n var items = [ cloneItem(item) ];\n for (var i = 0; i < $scope.saveOptions.addMulti - 2; i++) {\n items.push(cloneItem(items[i]));\n }\n\n var extraItemCount = 0;\n\n $scope.saveOptions.addMulti = 1;\n\n var lastPromise = items.reduce(\n function(itemPromise,curItem){\n return itemPromise.then(\n function () {\n return curItem.$create().then(\n function(newItem){\n extraItemCount++;\n if(target_mfhd_id)\n return newItem.$relinkMfhd( {target_mfhd_id: target_mfhd_id});\n else\n return newItem;\n }, function(fail){\n console.warn('failed multi Item create.');\n console.log( fail ); // FIXME: force refresh.\n return $q.reject(fail); }\n )}\n )}, $q.when(true));\n\n return lastPromise.then(function(lastItem){\n alertService.add({type: \"success\",\n msg: extraItemCount + ' additional items added.' });\n editorRV.doNotClose = false;\n editorRV.multi = items;\n holdings.addItem( items.concat( newItem ) );\n\n return editorRV;\n\n }, function(err){\n editorRV.error = true;\n alertService.addApiError(err,'Error encountered in Add Multiple Copies');\n });\n\n }\n }).finally(function(val){\n if (!editorRV.doNotClose) $uibModalInstance.close(editorRV);\n });\n\n }\n };\n function saveError (e) {\nconsole.warn(e);\n var msg = \"There was an error saving the item record: \";\n\n var data;\n try{ data = JSON.parse(e.data); } catch(err) { data = e.data; }\n var field = data.constraint;\n\n if(e.status == '409'){\n editorRV.doNotClose = true; // Leave the modal open on duplicates.\n msg += data.message + \" '\" + data.value + \"' violates \" + data.constraint + ' constraint.';\n // This is only for barcode atm. (FIXME)\n if($scope.itemForm && $scope.itemForm[field]) $scope.itemForm[field].$setValidity('duplicate', false);\n var unwatch = $scope.$watch( 'item.'+field, function(nv,ov){\n if(nv != ov){\n // assume any change is ok.\n $scope.itemForm[field].$setValidity('duplicate', true);\n unwatch();\n }\n });\n } else {\n msg += e.statusText + ' ' + e.data;\n }\n alertService.add( { msg: msg, type: \"error\"});\n }\n\n $scope.itemTemplates = kwApi.ItemTemplate.\n getList( { branchcode: userService.login_branch }).\n sort(function(a,b){ return a.name.localeCompare(b.name);});\n $scope.activeDefaults = {\n tmpl: null,\n scope: 'item' // { item, session }.\n };\n\n $scope.setTmplScope = function(){\n var scope = $scope.activeDefaults.scope;\n $scope.activeDefaults.scope = (scope=='session') ? 'session' : 'item';\n if(scope=='session'){\n userService.clientSession.newItemTemplate = $scope.activeDefaults.tmpl;\n } else {\n userService.clientSession.newItemTemplate = null;\n if(scope=='delete'){\n var i = $scope.itemTemplates.indexOf($scope.activeDefaults.tmpl);\n if(i<0) console.warn( 'no find tmpl ');\n $scope.activeDefaults.tmpl.$delete({}, function(tmpl){\n $scope.itemTemplates.splice(i,1);\n tmpl.id = undefined;\n });\n } else if(scope=='clear'){\n $scope.item.$applyDefaults(newItemDefaults());\n $scope.activeDefaults.tmpl = null;\n }\n }\n };\n $scope.applyTmpl = function(tmpl){\n\n $scope.vis.optionsDrop = false;\n // default values override all extant values (for create only)\n $scope.activeDefaults.tmpl = tmpl;\n if(!$scope.activeDefaults.scope) $scope.activeDefaults.scope = 'item';\n // sterilize against bad tmpl data.\n configService.ItemFields.filter( function(f){ return f.editor.readonly; }\n ).map( function(field){ return field.code; }\n ).forEach(function(ro_field){ delete tmpl[ro_field] ;});\n\n $scope.item.$applyDefaults(tmpl.template);\n };\n\n if(userService.clientSession.newItemTemplate){\n if( !itemid )\n $scope.applyTmpl(userService.clientSession.newItemTemplate);\n $scope.activeDefaults.scope = 'session';\n }\n $scope.saveTmpl = function(){\n var tmplName = ($scope.activeDefaults.tmpl||{}).name;\n kohaDlg.dialog({\n heading: \"Save default values\",\n message: \"Save item defaults for this session ?\",\n inputs: [{ name: 'name', label: 'Name', val: tmplName },\n { name: 'save' , label: 'Save for future sessions?', type: 'checkbox' }],\n buttons: [{val: true, label: 'Save', btnClass: 'btn-primary'},\n {val: false, label: 'Cancel'}],\n }).result.then(function(rv) {\n if (rv) {\n var tmpldata = {};\n\n angular.forEach($scope.item, function(v,k){\n if( !bvItemSvc.fieldDef[k] || bvItemSvc.fieldDef[k].editor.readonly ){\n return; // next;\n }\n if(v) tmpldata[k] = v; // This won't override itemfield defaults.\n });\n if($scope.activeDefaults.tmpl){\n $scope.activeDefaults.tmpl.name = rv.name;\n $scope.activeDefaults.tmpl.template = tmpldata;\n } else {\n $scope.activeDefaults.tmpl = new kwApi.ItemTemplate( {\n name: rv.name,\n template: tmpldata,\n branchcode: userService.login_branch\n });\n }\n userService.clientSession.newItemTemplate = $scope.activeDefaults.tmpl;\n if(rv.save){\n if($scope.activeDefaults.tmpl.id){\n $scope.activeDefaults.tmpl.$put();\n } else {\n $scope.activeDefaults.tmpl.$create({}, function(tmpl){\n $scope.itemTemplates.push(tmpl);\n $scope.activeDefaults.scope = 'session';\n });\n }\n }\n }\n });\n };\n\n}])\n\n.controller('StaffSubstituteUserCtrl', [\"$scope\", \"configService\", \"roles\", \"vendors\", \"branches\", \"$location\", \"$timeout\", \"kwApi\", \"alertService\", \"userService\", \"mode\", \"$q\", function($scope, configService, roles, vendors, branches, $location, $timeout,\n kwApi, alertService, userService, mode, $q) {\n\n $scope.currentUser = userService.username;\n $scope.config = configService;\n $scope.roles = roles;\n $scope.branches = branches.sort(function(a,b) {\n return (a.branchname < b.branchname ? -1 : a.branchname > b.branchname ? 1 : 0)\n });\n $scope.vendors = vendors;\n $scope.mode = mode || 'specific';\n\n try {\n $scope.branchParameters = JSON.parse(configService.TemplateBranchParameters);\n }\n catch (e) {\n $scope.branchParameters = [];\n console.warn(e);\n }\n\n try {\n $scope.userParameters = JSON.parse(configService.TemplateUserParameters);\n }\n catch (e) {\n $scope.userParameters = [];\n console.warn(e);\n }\n\n var checkValid = function(newVal) {\n $scope.isValid = true;\n var errors = [];\n if ($scope.mode == 'specific') {\n if (!newVal.user) {\n $scope.isValid = false;\n errors.push(\"User\");\n }\n }\n else if ($scope.mode == 'generic-user') {\n if (!newVal.branch) {\n $scope.isValid = false;\n errors.push(\"Branch\");\n }\n if (!newVal.category) {\n $scope.isValid = false;\n errors.push(\"Category\");\n }\n $scope.userParameters.forEach(function(p) {\n if (p.required && !newVal.userParameter[p.field]) {\n $scope.isValid = false;\n errors.push(p.label);\n }\n });\n }\n else if ($scope.mode == 'generic-branch') {\n if (!newVal.category) {\n $scope.isValid = false;\n errors.push(\"Category\");\n }\n $scope.userParameters.forEach(function(p) {\n if (p.required && !newVal.userParameter[p.field]) {\n $scope.isValid = false;\n errors.push(p.label);\n }\n });\n $scope.branchParameters.forEach(function(p) {\n if (p.required && !newVal.branchParameter[p.field]) {\n $scope.isValid = false;\n console.log(\"Missing \" + p.field);\n errors.push(p.label);\n }\n });\n }\n $scope.missing = (errors.length ? errors.join(', ') : null);\n };\n\n $scope.su = {\n userRoles: [],\n branchParameter: {},\n userParameter: {},\n };\n\n $scope.$watch('su', function(newVal) {\n checkValid($scope.su);\n }, true);\n\n $scope.$watch('mode', function(newVal) {\n checkValid($scope.su);\n });\n\n $scope.patronSelectConfig = {\n load: function(query, callback) {\n kwApi.Patron.getSuEligibleList({searchValue: query}).$promise.then(function(rv) {\n callback(rv);\n }, function(err) {\n callback();\n });\n },\n loadThrottle: 600,\n maxItems: 1,\n valueField: 'borrowernumber',\n labelField: 'firstname', \n searchField: ['firstname','surname','email','userid','card_number'],\n render: {\n item: function(item, escape) {\n if (!item) return '';\n return '
    ' + item.firstname + ' ' + item.surname + ' (' + item.branch_name + ')
    ';\n },\n option: function(item, escape) {\n if (!item) return '';\n var deets = [];\n if (item.card_number)\n deets.push('#' + item.card_number);\n else\n deets.push('(no card number)');\n\n if (item.userid && item.userid != item.card_number)\n deets.push(item.userid);\n if (item.email)\n deets.push(item.email);\n return '
    ' + item.firstname + ' ' + item.surname + ' (' + item.branch_name + ')
    '\n + '
    ' + deets.join(' - ') + '
    ';\n }\n }\n };\n\n $scope.branchSelectConfig = {\n maxItems: 1,\n valueField: 'branchcode',\n labelField: 'branchname',\n searchField: ['branchname','branchcode'],\n };\n \n $scope.roleSelectConfig = {\n valueField: 'id',\n labelField: 'name',\n searchField: ['name'],\n };\n\n if (!$scope.mode)\n $scope.mode = 'specific';\n\n $scope.canGenericBranch = userService.can({substitute_user: 'generic'});\n $scope.canGenericUser = userService.can({substitute_user: {generic: 'branch'}});\n $scope.canSpecificUser = userService.can({substitute_user: 'specific'});\n\n\n $scope.categories = [];\n angular.forEach(configService.patroncats,function(val,key) {\n if ($scope.canGenericBranch || (val.category_type != 'S')) {\n $scope.categories.push({label: val.description, value: key});\n }\n });\n\n var makeGeneric = function() {\n var user = {};\n if ($scope.mode == 'generic-user') {\n user.branchcode = $scope.su.branch;\n }\n else {\n user.branch = {};\n angular.forEach($scope.su.branchParameter, function(val, key) {\n var matches = key.match(/^attributes\\.(.+)/);\n if (matches && matches.length == 2) {\n if (!user.branch.attributes)\n user.branch.attributes = {};\n user.branch.attributes[matches[1]] = val;\n }\n else {\n user.branch[key] = val;\n }\n });\n }\n\n angular.forEach($scope.su.userParameter, function(val, key) {\n user[key] = val;\n });\n\n user.roles = [];\n angular.forEach($scope.su.userRoles, function(role) {\n user.roles.push(role);\n });\n\n return kwApi.Patron.makeTemplatePatron({},user).$promise;\n };\n\n var makeTicket = function() {\n var promise;\n if ($scope.mode == 'specific') {\n promise = $q.when({id: $scope.su.user});\n }\n else {\n promise = makeGeneric();\n }\n return promise.then(function(member) {\n console.log(\"Switching to member ID \" + member.id);\n return kwApi.Patron.generateSuTicket({id: member.id},{}).$promise;\n });\n }\n\n $scope.openCurrentSession = function() {\n makeTicket().then(function(rv) {\n userService.applyProxyTicket(rv.ticket);\n $scope.resetSession();\n });\n };\n\n $scope.openNewSession = function() {\n makeTicket().then(function(rv) {\n var ticket = rv.ticket;\n var url = $location.protocol() + '://' + $location.host();\n if ($location.port() != 80) {\n url = url + ':' + $location.port();\n }\n $scope.ticketUrl = url + '/?proxy_ticket=' + rv.ticket;\n });\n };\n\n $scope.resetSession = function() {\n $timeout(function() {\n $scope.ticketUrl = null;\n }, 500);\n };\n}])\n\n// FIXME - this is going to become intractable. We should consider loading and unloading staff apps as needed\n// and move some of the common stuff to services\n\n.controller('StaffVendorsCtrl', [\"$scope\", \"$state\", \"vendors\", \"messageBox\", \"alertService\", \"loading\", \"$uibModal\", \"kwApi\", \"kwFileUploadSvc\", function($scope, $state, vendors, messageBox, alertService, loading, $uibModal, kwApi, kwFileUploadSvc) {\n $scope.load = function(list) {\n angular.forEach(list, function(v) {\n if (v.status == 'Unavailable') {\n if (v.status_message)\n v.status = 'Unavailable - Visible';\n else\n v.status = 'Unavailable - Hidden';\n }\n \n var pmp = v.price_model_parameters;\n v.vd_breakpoint = pmp.breakpoint_type == '<' ? 'End Before' :\n pmp.breakpoint_type == '<=' ? 'End w/Incl' :\n pmp.breakpoint_type == '>' ? 'Start After' :\n pmp.breakpoint_type == '>=' ? 'Start w/Incl' : pmp.breakpoint_type;\n\n v.vd_basis = pmp.basis == 'none' ? 'None' :\n pmp.basis == 'count' ? 'Item Count' :\n pmp.basis == 'list' ? 'Total List' :\n pmp.basis == 'net' ? 'Total Disc' : pmp.basis;\n\n v.vd_method = pmp.volume_discount_overrides_base == '1' ? 'Superseding' : 'Cumulative'\n });\n if ($scope.vendors)\n $scope.vendors.replaceWith(list);\n else \n $scope.vendors = list;\n };\n\n $scope.load(vendors);\n\n $scope.reload = function() {\n loading.add();\n kwApi.Vendor.getList().$promise.then(function(data) {\n loading.resolve();\n $scope.load(data);\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Reload failed');\n });\n };\n\n $scope.update = function(sels) {\n $state.go('staff.admin.vendor', {vendor_id: sels[0].id});\n };\n\n $scope.ledgers = function(sels) {\n $state.go('staff.admin.vendor-ledgers', {vendor_id: sels[0].id});\n };\n\n $scope.deprecate = function(sels) {\n messageBox.confirm({\n title: \"Are you sure?\",\n message: \"Are you sure you want to deprecate \" + (sels.length>1 ? 'these vendors' : 'this vendor') + '?'\n }).then(function(result) {\n loading.add();\n var ids = [];\n angular.forEach(sels, function(sel) { ids.push(sel.id); });\n kwApi.Vendor.deprecateAll({ids: ids}).$promise.then(function() {\n loading.resolve();\n $scope.reload();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Deprecate failed');\n });\n });\n };\n\n $scope.purge = function(sels) {\n messageBox.confirm({\n title: \"Are you sure?\",\n message: \"Are you sure you want to purge \" + (sels.length>1 ? 'these vendors' : 'this vendor') + '? This will PERMANENTLY delete all associated bibs and items!'\n }).then(function(result) {\n loading.add();\n var ids = [];\n angular.forEach(sels, function(sel) { ids.push(sel.id); });\n kwApi.Vendor.purgeAll({ids: ids}).$promise.then(function() {\n loading.resolve();\n $scope.reload();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Purge failed');\n });\n });\n };\n\n $scope.create = function() {\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/vendors/detail.html',\n dialogClass: 'xmodal',\n backdropClass: 'modal-backdrop',\n controller: 'StaffVendorsDetailCtrl',\n resolve: {\n vendor: function() { return {\n price_model_parameters: {\n basis: 'none',\n breakpoint_type: '>=',\n volume_discount_overrides_base: '0',\n },\n admin_percent: 0,\n status: 'Available',\n } },\n },\n size: 'lg',\n backdropClick: false,\n }).result.then(function() {\n $scope.reload();\n });\n };\n\n\n $scope.classTable = function(sels) {\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/vendors/vendor-classification-table.html',\n dialogClass: 'xmodal',\n backdropClass: 'modal-backdrop',\n backdropClick: false,\n controller: [\"$scope\", \"ctable\", \"vendor\", function($scope, ctable, vendor) {\n $scope.ctable = ctable;\n $scope.applyTable = function(rows) {\n $scope.ctable.length = 0;\n angular.forEach(rows, function(row) {\n $scope.ctable.push({branch_code: row[0], classification: row[1]});\n });\n };\n $scope.save = function() {\n loading.add();\n kwApi.Vendor.setClassificationTable({id: vendor.id}, {table: $scope.ctable}).$promise.then(function() {\n loading.resolve();\n $scope.$close();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Update failed');\n });\n };\n }],\n resolve: {\n vendor: function() { return sels[0]; },\n ctable: function() { \n return kwApi.Vendor.getClassificationTable({id: sels[0].id}).$promise;\n },\n },\n }).result.then(function(data) {\n $scope.reload();\n });\n };\n\n\n $scope.download = function(sels) {\n var ids = [];\n angular.forEach(sels, function(s) { ids.push(s.id) });\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/vendors/vendors-download.html',\n dialogClass: 'xmodal',\n backdropClass: 'modal-backdrop',\n backdropClick: false,\n controller: [\"$scope\", function($scope) {\n $scope.scopeOptions = [{display: 'All vendors', value: 'all'}];\n if (sels.length>0)\n $scope.scopeOptions.push({display: 'Selection only (' + sels.length + ' vendors)', value: 'selected'});\n else\n $scope.scopeOptions.push({display: 'Headers only', value: 'selected'});\n\n $scope.formdata = {\n format: 'xlsx',\n scope: 'selected',\n vendor_ids: ids.join('.'),\n };\n $scope.vendor_count = sels.length;\n }]\n });\n };\n\n $scope.downloadVdTables = function(sels) {\n var ids = [];\n angular.forEach(sels, function(s) { ids.push(s.id) });\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/vendors/vendors-download-vd-tables.html',\n dialogClass: 'xmodal',\n backdropClass: 'modal-backdrop',\n backdropClick: false,\n controller: [\"$scope\", function($scope) {\n $scope.scopeOptions = [{display: 'All vendors', value: 'all'}];\n if (sels.length>0)\n $scope.scopeOptions.push({display: 'Selection only (' + sels.length + ' vendors)', value: 'selected'});\n else\n $scope.scopeOptions.push({display: 'Headers only', value: 'selected'});\n\n $scope.formdata = {\n format: 'xlsx',\n scope: 'selected',\n vendor_ids: ids.join('.'),\n };\n $scope.vendor_count = sels.length;\n }]\n });\n };\n\n $scope.upload = function() {\n kwFileUploadSvc.upload({\n title: 'Upload Vendors',\n description: 'Choose a spreadsheet in Excel (.xlsx) or CSV (.csv) format. You can download a '\n + 'set of headers first to set up your spreadsheet (unhighlight any rows and click '\n + 'the download button)',\n instructions: true,\n formdata: {\n format: '',\n update_action: 'update'\n },\n inputs: [\n {name: 'format', type: 'select', label: 'Format', \n instructions: \"Select the file format (Excel or comma-separated value), or use Automatic \" \n + \"and the format will be assumed from the file extension (.csv or .xslx)\",\n values: [\n {value: '', display: 'Automatic (from file extension)'},\n {value: 'xlsx', display: 'Excel (.xlsx)'},\n {value: 'csv', display: 'Comma-separated (.csv)'}\n ]\n },\n {name: 'update_action', type: 'select', label: 'Action', \n instructions: \"Update - add or replace vendor records (match on Vendor Code). \"\n + \"Update / Delete - requires a Delete column; if non-empty, any matching vendor \" \n + \"(match on Vendor Code) will be deleted. \"\n + \"Add Only - only add new ledgers; any match on Vendor Code is an error. \" \n + \"Replace - completely replace all vendors.\",\n values: [\n {value: 'update', display: 'Update'},\n {value: 'update_delete', display: 'Update / Delete'},\n {value: 'insert', display: 'Add Only'},\n {value: 'replace', display: 'Replace'},\n ]\n },\n ],\n url: '/api/vendor?op=upload'\n }).then(function() {\n $scope.reload();\n });\n };\n\n $scope.uploadVdTables = function() {\n kwFileUploadSvc.upload({\n title: 'Upload Discount Tables',\n description: 'Choose a spreadsheet in Excel (.xlsx) or CSV (.csv) format. You can download a '\n + 'set of headers first to set up your spreadsheet (unhighlight any rows and click '\n + 'the download button). Each spreadsheet row must include a valid Vendor Code, '\n + 'Level, and Amount, and may include volume discount parameters (Volume Discount '\n + 'Basis, Volume Discount Type, and Breakpoint Type)',\n instructions: true,\n formdata: {\n format: '',\n },\n inputs: [\n {name: 'format', type: 'select', label: 'Format', \n instructions: \"Select the file format (Excel or comma-separated value), or use Automatic \" \n + \"and the format will be assumed from the file extension (.csv or .xslx)\",\n values: [\n {value: '', display: 'Automatic (from file extension)'},\n {value: 'xlsx', display: 'Excel (.xlsx)'},\n {value: 'csv', display: 'Comma-separated (.csv)'}\n ]\n },\n ],\n url: '/api/vendor/?op=upload-vd-table'\n }).then(function() {\n $scope.reload();\n });\n\n };\n}])\n\n.controller('StaffVendorsDetailCtrl', [\"$scope\", \"$state\", \"vendor\", \"kwApi\", \"loading\", \"alertService\", function($scope, $state, vendor, kwApi, loading, alertService) {\n $scope.isUpdate = (vendor.id ? true : false);\n $scope.vendor = vendor;\n\n if ($scope.vendor.status == 'Unavailable') {\n if ($scope.vendor.status_message)\n $scope.vendor.status = 'Unavailable - Visible';\n else\n $scope.vendor.status = 'Unavailable - Hidden';\n }\n\n vendor.admin_percent = '' + Math.floor(1000*vendor.admin_percent)/10;\n\n if (!$scope.vendor.price_model_parameters)\n $scope.vendor.price_model_parameters = {};\n if (!$scope.vendor.price_model_parameters.volume_discount_structure)\n $scope.vendor.price_model_parameters.volume_discount_structure = [];\n\n angular.forEach($scope.vendor.price_model_parameters.volume_discount_structure, function(ent) {\n ent.amount = '' + Math.floor(1000*ent.amount)/10;\n });\n\n $scope.addDiscountTier = function() {\n $scope.vendor.price_model_parameters.volume_discount_structure.push({level: '0.00', amount: '0.00'});\n };\n $scope.rmDiscountTier = function(i) {\n $scope.vendor.price_model_parameters.volume_discount_structure.splice(i,1);\n };\n\n $scope.cancel = function() {\n if ($scope.isUpdate) {\n $state.go('staff.admin.vendors');\n }\n else {\n console.log(\"Executing close\");\n $scope.$close();\n }\n };\n\n $scope.save = function(data) {\n data = angular.copy(data);\n console.log(\"Save\");\n if (data.status == 'Unavailable - Hidden') {\n data.status = 'Unavailable';\n data.status_message = '';\n }\n else if (data.status == 'Unavailable - Visible') {\n if (!data.status_message)\n data.status_message = 'Not Available';\n }\n data.admin_percent = data.admin_percent / 100;\n angular.forEach(data.price_model_parameters.volume_discount_structure, function(ent) {\n ent.amount = ent.amount/100;\n });\n\n loading.add();\n //console.dir(data);\n var saved = ($scope.isUpdate ? kwApi.Vendor.put({id: data.id}, data) : kwApi.Vendor.save(data));\n saved.$promise.then(function() {\n loading.resolve();\n if ($scope.isUpdate) {\n $state.go('staff.admin.vendors');\n }\n else {\n $scope.$close();\n }\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Save failed');\n });\n };\n\n $scope.setDiscountTable = function(data) {\n $scope.vendor.price_model_parameters.volume_discount_structure.length = 0;\n angular.forEach(data, function(row) {\n $scope.vendor.price_model_parameters.volume_discount_structure.push({level: row[0], amount: row[1]});\n });\n };\n}])\n\n.controller('StaffVendorsLedgersCtrl', [\"$scope\", \"$state\", \"vendor\", \"ledgers\", \"ledgerPriceModels\", \"messageBox\", \"alertService\", \"loading\", \"$uibModal\", \"kwApi\", \"kwFileUploadSvc\", \"$q\", \"kwImportFactorySvc\", function($scope, $state, vendor, ledgers, ledgerPriceModels, messageBox, alertService, loading, $uibModal, kwApi, kwFileUploadSvc, $q, kwImportFactorySvc) {\n $scope.vendor = vendor;\n\n $scope.load = function(list) {\n list.forEach(function(e) {\n e.display_status = e.access_status;\n });\n\n if ($scope.ledgers)\n $scope.ledgers.replaceWith(list);\n else \n $scope.ledgers = list;\n };\n\n $scope.load(ledgers);\n\n $scope.reload = function() {\n loading.add();\n kwApi.Vendor.getLedgers({id: vendor.id}).$promise.then(function(data) {\n loading.resolve();\n $scope.load(data);\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Reload failed');\n });\n };\n\n $scope.back = function() {\n $state.go('staff.admin.vendors');\n };\n\n $scope.update = function(sels) {\n $state.go('staff.admin.vendor-ledger', {vendor_id: vendor.id, ledger_id: sels[0].id});\n };\n\n $scope.deprecate = function(sels) {\n messageBox.confirm({\n title: \"Are you sure?\",\n message: \"Are you sure you want to deprecate \" + (sels.length>1 ? 'these ledgers' : 'this ledger') + '?'\n }).then(function(result) {\n loading.add();\n var ids = [];\n angular.forEach(sels, function(sel) { ids.push(sel.id); });\n kwApi.Ledger.deprecateAll({ids: ids}).$promise.then(function() {\n loading.resolve();\n $scope.reload();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Deprecate failed');\n });\n });\n };\n\n $scope.purge = function(sels) {\n messageBox.confirm({\n title: \"Are you sure?\",\n message: \"Are you sure you want to purge \" + (sels.length>1 ? 'these ledgers' : 'this ledger') + '? This will PERMANENTLY delete all associated bibs and items!'\n }).then(function(result) {\n loading.add();\n var ids = [];\n angular.forEach(sels, function(sel) { ids.push(sel.id); });\n kwApi.Ledger.purgeAll({ids: ids}).$promise.then(function() {\n loading.resolve();\n $scope.reload();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Purge failed');\n });\n });\n };\n\n $scope.create = function() {\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/vendors/ledger-detail.html',\n dialogClass: 'xmodal',\n backdropClass: 'modal-backdrop',\n backdropClick: false,\n controller: 'StaffVendorsLedgerDetailCtrl',\n resolve: {\n vendor: function() { return vendor; },\n ledger: function() { return {\n price_model: 'BaseItem DiscountNone',\n is_default: '0',\n price_model_parameters: {},\n vendor_code: vendor.id,\n access_status: 'Available',\n pricing_status: 'Available',\n product_type: 'OTAC',\n product_subtype: 'PnM',\n } },\n ledgerPriceModels: [\"$stateParams\", \"kwApi\", function($stateParams, kwApi) {\n return kwApi.Ledger.getPriceModelConfig({id: $stateParams.ledger_id}).$promise;\n }],\n },\n size: 'lg',\n }).result.then(function() {\n $scope.reload();\n });\n };\n\n $scope.pricing = function(sels) {\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/vendors/ledger-pricing.html',\n dialogClass: 'xmodal',\n backdropClass: 'modal-backdrop',\n backdropClick: false,\n controller: 'StaffVendorsLedgerPricingCtrl',\n resolve: {\n ledgerPriceModels: function() { return ledgerPriceModels; },\n vendor: function() { return vendor; },\n ledger: function() { \n var ldg = sels[0];\n ldg.price_models = ldg.price_model.split(' ');\n return ldg;\n },\n doSave: function() { return true; }\n },\n size: 'lg',\n }).result.then(function(data) {\n $scope.reload();\n });\n };\n\n $scope.download = function(sels) {\n var ids = [];\n angular.forEach(sels, function(s) { ids.push(s.id) });\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/vendors/ledgers-download.html',\n dialogClass: 'xmodal',\n backdropClass: 'modal-backdrop',\n backdropClick: false,\n controller: [\"$scope\", function($scope) {\n $scope.scopeOptions = [{display: 'All ledgers for this vendor', value: 'all'}];\n if (sels.length>0)\n $scope.scopeOptions.push({display: 'Selection only (' + sels.length + ' ledgers)', value: 'selected'});\n else\n $scope.scopeOptions.push({display: 'Headers only', value: 'selected'});\n\n $scope.formdata = {\n format: 'xlsx',\n scope: 'selected',\n ledger_ids: ids.join('.'),\n };\n $scope.vendor_id = vendor.id;\n $scope.ledger_count = sels.length;\n }]\n });\n };\n\n $scope.downloadPricing = function(sels) {\n var ids = [];\n angular.forEach(sels, function(s) { ids.push(s.id) });\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/vendors/ledgers-download-pricing.html',\n dialogClass: 'xmodal',\n backdropClass: 'modal-backdrop',\n backdropClick: false,\n controller: [\"$scope\", function($scope) {\n $scope.scopeOptions = [{display: 'All ledgers for this vendor', value: 'all'}];\n if (sels.length>0)\n $scope.scopeOptions.push({display: 'Selection only (' + sels.length + ' ledgers)', value: 'selected'});\n else\n $scope.scopeOptions.push({display: 'Headers only', value: 'selected'});\n\n $scope.formdata = {\n format: 'xlsx',\n scope: 'selected',\n type: 'discount',\n ledger_ids: ids.join('.'),\n };\n $scope.vendor_id = vendor.id;\n $scope.ledger_count = sels.length;\n }]\n });\n };\n\n $scope.upload = function() {\n kwFileUploadSvc.upload({\n title: 'Upload Ledgers',\n description: 'Choose a spreadsheet in Excel (.xlsx) or CSV (.csv) format. You can download a '\n + 'set of headers first to set up your spreadsheet (unhighlight any rows and click '\n + 'the download button)',\n instructions: true,\n formdata: {\n format: '',\n update_action: 'update'\n },\n inputs: [\n {name: 'format', type: 'select', label: 'Format', \n instructions: \"Select the file format (Excel or comma-separated value), or use Automatic \" \n + \"and the format will be assumed from the file extension (.csv or .xslx)\",\n values: [\n {value: '', display: 'Automatic (from file extension)'},\n {value: 'xlsx', display: 'Excel (.xlsx)'},\n {value: 'csv', display: 'Comma-separated (.csv)'}\n ]\n },\n {name: 'update_action', type: 'select', label: 'Action', \n instructions: \"Update - add or replace ledger records (match on Ledger Code). \"\n + \"Update / Delete - requires a Delete column; if non-empty, any matching ledger \" \n + \"(match on Ledger Code) will be deleted. \"\n + \"Add Only - only add new ledgers; any match on Ledger Code is an error. \" \n + \"Replace - completely replace all ledgers for this vendor.\",\n values: [\n {value: 'update', display: 'Update'},\n {value: 'update_delete', display: 'Update / Delete'},\n {value: 'insert', display: 'Add Only'},\n {value: 'replace', display: 'Replace'},\n ]\n },\n ],\n url: '/api/vendor/' + vendor.id + '/ledgers?op=upload'\n }).then(function() {\n $scope.reload();\n });\n };\n\n $scope.uploadPricing = function() {\n kwFileUploadSvc.upload({\n title: 'Upload Price Tables',\n description: 'Choose a spreadsheet in Excel (.xlsx) or CSV (.csv) format. You can download a '\n + 'set of headers first to set up your spreadsheet (unhighlight any rows and click '\n + 'the download button). Each spreadsheet row must include a valid ledger code '\n + 'in addition to the pricing information',\n instructions: true,\n formdata: {\n format: '',\n type: 'discount',\n criteria: 'table',\n },\n inputs: [\n {name: 'format', type: 'select', label: 'Format', \n instructions: \"Select the file format (Excel or comma-separated value), or use Automatic \" \n + \"and the format will be assumed from the file extension (.csv or .xslx)\",\n values: [\n {value: '', display: 'Automatic (from file extension)'},\n {value: 'xlsx', display: 'Excel (.xlsx)'},\n {value: 'csv', display: 'Comma-separated (.csv)'}\n ]\n },\n {name: 'type', type: 'select', label: 'Table Type', \n instructions: \"Choose either 'List Price' to upload a table to determine list pricing, or \"\n + \"'Discount' to upload a table to determine discount percentage.\",\n values: [\n {value: 'base', display: 'List Price'},\n {value: 'discount', display: 'Discount'},\n ]\n },\n {name: 'criteria', type: 'select', label: 'Criteria', \n instructions: \"Choose either 'From Table' to automatically determine the price criteria \"\n + \"from the table, or 'From Ledger' to use whatever criteria are already \"\n + \"specified in the ledger price parameters\",\n values: [\n {value: 'ledger', display: 'From Ledger'},\n {value: 'table', display: 'From Table'},\n ]\n },\n ],\n url: '/api/vendor/' + vendor.id + '/ledgers?op=upload-pricing'\n }).then(function() {\n $scope.reload();\n });\n };\n\n $scope.factory = function(sels) {\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/vendors/ledgers-select-factory.html',\n dialogClass: 'xmodal',\n backdropClass: 'modal-backdrop',\n backdropClick: false,\n resolve: {\n scripts: function() {\n var deferred = $q.defer();\n kwApi.ImportScript.getList().$promise.then(function(data) {\n var scripts = [];\n angular.forEach(data, function(d) {\n if (d.script_type == 'sheet-single') {\n d._script_category = \"Spreadsheet\";\n }\n else if (d.script_type == 'sheet-full') {\n d._script_category = \"Spreadsheet\";\n }\n else {\n d._script_category = \"MARC\";\n }\n if (d.primary_vendor_code === null)\n d._primary_vendor_code = '(any vendor)';\n else \n d._primary_vendor_code = d.primary_vendor_code;\n if (d.embed_primary_ledger_code === null)\n d._embed_primary_ledger_code = '(any ledger)';\n else\n d._embed_primary_ledger_code = d.embed_primary_ledger_code;\n\n });\n angular.forEach(data, function(d) {\n if (d.primary_ledger_id == sels[0].id) {\n scripts.push({\n value: d,\n display: (d.name + ' - ' + d._script_category + ' (' + d._primary_vendor_code + ' / ' + d._embed_primary_ledger_code + ') ' )\n });\n }\n });\n if (scripts.length > 0) {\n scripts[0].selected = 1;\n }\n\n angular.forEach(data, function(d) {\n if (d.primary_ledger_id != sels[0].id) {\n scripts.push({\n value: d.id,\n display: (d.name + ' - ' + d.script_type + ' (' + d.primary_vendor_code + ' / ' + d.embed_primary_ledger_code + ') ' )\n });\n }\n });\n deferred.resolve(scripts);\n }, function() { deferred.reject() });\n return deferred.promise;\n }\n },\n controller: [\"$scope\", \"scripts\", function($scope, scripts) {\n $scope.scripts = scripts;\n $scope.formdata = {};\n angular.forEach(scripts, function(s) {\n if (s.selected)\n $scope.formdata.script = s.value;\n });\n }]\n }).result.then(function(data) {\n if (data) {\n kwImportFactorySvc.executeScript(data, sels[0].vendor_code, sels[0].id)\n }\n });\n };\n\n\n}])\n\n.controller('StaffVendorsLedgerDetailCtrl', [\"$scope\", \"$state\", \"vendor\", \"ledger\", \"ledgerPriceModels\", \"kwApi\", \"loading\", \"alertService\", \"$uibModal\", function($scope, $state, vendor, ledger, ledgerPriceModels, kwApi, loading, alertService, $uibModal) {\n //console.dir(ledgerPriceModels);\n $scope.isUpdate = (ledger.id ? true : false);\n $scope.vendor = vendor;\n $scope.ledger = ledger;\n\n\n $scope.oldStatus = ($scope.isUpdate ? $scope.ledger.access_status : '');\n $scope.indexBibs = '1';\n\n $scope.subtypes = {\n 'OTAC': [\n {id: 'PnM', value: 'Pick and Mix (PnM)'},\n {id: 'DDA', value: 'Demand-Driven Acquisition (DDA)'}\n ],\n 'SUB': [\n {id: 'SUB', value: 'Subscription (SUB)'}\n ],\n 'UPAK': [\n {id: 'UPOT', value: 'One Time (UPOT)'},\n {id: 'UPR', value: 'Renewable (UPR)'}\n ]\n };\n\n $scope.price_models = ledgerPriceModels;\n $scope.selectConfig = {\n valueField: 'model',\n labelField: 'title', \n searchField: ['title'],\n create: false,\n };\n\n if ($scope.vendor.status == 'Unavailable') {\n if ($scope.vendor.status_message)\n $scope.vendor.status = 'Unavailable - Visible';\n else\n $scope.vendor.status = 'Unavailable - Hidden';\n }\n vendor.admin_percent = '' + Math.floor(1000*vendor.admin_percent)/10;\n\n if (!$scope.vendor.price_model_parameters)\n $scope.vendor.price_model_parameters = {};\n if (!$scope.vendor.price_model_parameters.volume_discount_structure)\n $scope.vendor.price_model_parameters.volume_discount_structure = [];\n\n angular.forEach($scope.vendor.price_model_parameters.volume_discount_structure, function(ent) {\n ent.amount = '' + Math.floor(1000*ent.amount)/10;\n });\n\n $scope.ledger.price_models = $scope.ledger.price_model.split(' ');\n // TODO ledger translations\n\n $scope.cancel = function() {\n if ($scope.isUpdate) {\n $state.go('staff.admin.vendor-ledgers', {vendor_id: vendor.id});\n }\n else {\n $scope.$close();\n }\n };\n\n $scope.save = function(data) {\n data = angular.copy(data);\n //console.dir(data);\n\n data.price_model = data.price_models.join(' ');\n\n if ($scope.oldStatus == $scope.ledger.access_status)\n $scope.indexBibs = '0';\n\n loading.add();\n var saved = ($scope.isUpdate ? kwApi.Ledger.put({id: data.id, reindex: $scope.indexBibs}, data) : kwApi.Ledger.save({reindex: $scope.indexBibs}, data));\n\n saved.$promise.then(function() {\n loading.resolve();\n if ($scope.isUpdate) {\n $state.go('staff.admin.vendor-ledgers', {vendor_id: vendor.id});\n }\n else {\n $scope.$close();\n }\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Save failed');\n });\n };\n\n $scope.pricing = function() {\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/vendors/ledger-pricing.html',\n dialogClass: 'xmodal',\n backdropClass: 'modal-backdrop',\n backdropClick: false,\n controller: 'StaffVendorsLedgerPricingCtrl',\n resolve: {\n ledgerPriceModels: function() { return ledgerPriceModels; },\n vendor: function() { return vendor; },\n ledger: function() { return $scope.ledger; },\n doSave: function() { return false; }\n },\n size: 'lg',\n }).result.then(function(data) {\n if (data) {\n $scope.ledger.price_model_parameters = data;\n }\n });\n };\n}])\n\n.controller('StaffVendorsLedgerPricingCtrl', [\"$scope\", \"ledger\", \"ledgerPriceModels\", \"kwApi\", \"loading\", \"alertService\", \"doSave\", function($scope, ledger, ledgerPriceModels, kwApi, loading, alertService, doSave) {\n var modelDefs = {};\n angular.forEach(ledgerPriceModels, function(m) {\n modelDefs[m.model] = m;\n });\n\n $scope.price_model_parameters = angular.copy(ledger.price_model_parameters);\n\n $scope.ledgerParameters = [];\n $scope.byIndex = {};\n angular.forEach(ledger.price_models, function(model) {\n var pdesc = modelDefs[model].parameter_descriptions;\n for (var i=0; i1 ? 'these scripts' : 'this script') + '?'\n }).then(function(result) {\n loading.add();\n var ids = [];\n angular.forEach(sels, function(sel) { ids.push(sel.id); });\n kwApi.ImportScript.deleteAll({ids: ids}).$promise.then(function() {\n loading.resolve();\n $scope.reload();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Delete failed');\n });\n });\n };\n}])\n\n\n.controller('StaffLedgerImportScriptletsCtrl', [\"$scope\", \"importScriptlets\", \"$state\", \"loading\", \"alertService\", \"messageBox\", \"kwApi\", \"$uibModal\", function($scope, importScriptlets, $state, loading, alertService, messageBox, kwApi, $uibModal) {\n $scope.importScriptlets = importScriptlets;\n $scope.importScriptlets.forEach(function(c) {\n c.is_approved = (c.tainted === null || c.tainted === 0 || c.tainted === '0') ? true : false;\n });\n $scope.update = function(sels) {\n $scope.editComponent(sels[0]).then(function() {\n $scope.reload();\n });\n };\n\n $scope.scripts = function() {\n $state.go('staff.tools.ledger-importer.scripts');\n };\n\n $scope.create = function() {\n $scope.editComponent({code:'', description:'', name:'', script_type: '', isNew: true}).then(function(newComponent) {\n $scope.reload();\n });\n };\n\n $scope.reload = function() {\n kwApi.ImportScriptlet.getList({view: 'index'}).$promise.then(function(data) {\n $scope.importScriptlets.replaceWith(data);\n $scope.importScriptlets.forEach(function(c) {\n c.is_approved = (c.tainted === null || c.tainted === 0 || c.tainted === '0') ? true : false;\n });\n });\n };\n\n $scope.delete = function(sels) {\n messageBox.confirm({\n title: \"Are you sure?\",\n message: \"Are you sure you want to delete \" + (sels.length>1 ? 'these components' : 'this component') + '?'\n }).then(function(result) {\n loading.add();\n var ids = [];\n angular.forEach(sels, function(sel) { ids.push(sel.id); });\n kwApi.ImportScriptlet.deleteAll({ids: ids}).$promise.then(function() {\n loading.resolve();\n $scope.reload();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Delete failed');\n });\n });\n };\n\n $scope.approve = function(sels) {\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/tools/ledger-importer/scriptlet-approve.html',\n }).result.then(function(pw) {\n if (pw) {\n var ids = [];\n angular.forEach(sels, function(sel) { ids.push(sel.id); });\n loading.wrap(\n kwApi.ImportScriptlet.approveAll({ids: ids, password: pw}).$promise,\n \"Unable to approve components\"\n ).then(function() {\n $scope.reload();\n });\n }\n });\n };\n}])\n\n.controller('StaffLedgerImportScriptCtrl', [\"$scope\", \"$state\", \"loading\", \"alertService\", \"kwApi\", \"importScript\", \"importScriptlets\", \"vendors\", \"$uibModal\", \"$q\", \"configService\", function($scope, $state, loading, alertService, kwApi, importScript, importScriptlets, vendors, $uibModal, $q, configService) {\n\n $scope.vendors = vendors;\n $scope.vendors.unshift({branchcode: null, branchname: '(No Associated Vendor)'});\n $scope.infomart = configService.infomart;\n\n $scope.ledgers = [];\n\n $scope.config = configService;\n\n importScript.components = {\n header: [],\n sheet: [],\n row: [],\n marc: [],\n bibselect: [],\n bib: [],\n bibfinalize: [],\n };\n var tainted = [];\n angular.forEach(importScript._embed.scriptlets, function(c) {\n if (c.tainted) {\n tainted.push(c.id);\n c.display_name = c.name + ' [Unapproved]';\n }\n else {\n c.display_name = c.name + ' [Approved]';\n }\n angular.forEach(c.parameters, function(p) {\n p.value = c.embed_parameter_values[p.name];\n });\n importScript.components[c.script_type].push(c);\n });\n delete importScript._embed;\n $scope.tainted = tainted.join(' ');\n\n $scope.importScript = importScript;\n if (importScript.primary_vendor_code) {\n kwApi.Vendor.getLedgers({id: importScript.primary_vendor_code}).$promise.then(function(ledgers) {\n $scope.ledgers = ledgers;\n });\n }\n\n $scope.availableComponents = {};\n\n $scope.loadScriptlets = function(recs) {\n $scope.availableComponents.length = 0;\n angular.forEach($scope.pipelineTypes, function(ptype) {\n var t = ptype.value;\n $scope.availableComponents[t] = [\n {display_name: 'New component (blank)...', description: 'New component...', isNew: true},\n {display_name: 'New component (from copy)...', description: 'New component (from copy)...', isNew: true, isCopy: true},\n ];\n });\n angular.forEach(recs, function(rec) {\n if (rec.script_type && $scope.availableComponents[rec.script_type]) {\n if (rec.tainted) {\n rec.display_name = rec.name + ' [Unapproved]';\n }\n else {\n rec.display_name = rec.name + ' [Approved]';\n }\n $scope.availableComponents[rec.script_type].push(rec);\n }\n });\n };\n\n $scope.reloadScriptlets = function() {\n var deferred = $q.defer();\n kwApi.ImportScriptlet.getList().$promise.then(function(data) {\n $scope.loadScriptlets(data);\n deferred.resolve(data);\n }, function() {\n deferred.reject();\n });\n return deferred.promise;\n };\n\n $scope.loadScriptlets(importScriptlets);\n\n $scope.save = function(orig) {\n var data = angular.copy(orig);\n delete data.components;\n\n var scriptlets = [];\n angular.forEach($scope.pipelineTypes, function(pt) {\n var t = pt.value;\n angular.forEach(orig.components[t], function(c) {\n if (c.id) {\n var scriptlet = {id: c.id, embed_parameter_values: {}};\n angular.forEach(c.parameters, function(p) {\n scriptlet.embed_parameter_values[p.name] = p.value;\n });\n scriptlets.push(scriptlet);\n }\n });\n });\n data.scriptlets = scriptlets;\n loading.add();\n var saved = (data.id ? kwApi.ImportScript.put({id: data.id}, data) : kwApi.ImportScript.save({}, data));\n saved.$promise.then(function(newRec) {\n loading.resolve();\n $state.go('staff.tools.ledger-importer.scripts');\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Save failed');\n });\n };\n\n $scope.cancel = function() {\n $state.go('staff.tools.ledger-importer.scripts');\n };\n\n $scope.$watch('importScript.components', function() {\n var tainted = [];\n $scope.importScript.is_approved = true;\n angular.forEach($scope.importScript.components, function(val, key) {\n val.forEach(function(c) {\n if (c.tainted !== 0 && c.tainted !== '0') {\n tainted.push(c.id);\n $scope.importScript.is_approved = false;\n }\n });\n });\n $scope.tainted = tainted.join(' ');\n }, true);\n\n\n $scope.editComponentAndReload = function(component) {\n var deferred = $q.defer();\n $scope.editComponent(component).then(function(data) {\n if (data) {\n data.display_name = data.name + ' [Unapproved]';\n angular.extend(component,data);\n $scope.reloadScriptlets().then(function() {\n deferred.resolve(data);\n }, function() {\n deferred.resolve(data);\n });\n }\n else {\n deferred.reject();\n }\n });\n return deferred.promise;\n };\n\n $scope.newComponent = {\n header: null,\n sheet: null,\n row: null,\n marc: null,\n bibselect: null,\n bib: null,\n bibfinalize: null,\n };\n\n $scope.$watch('newComponent', function(newVal, oldVal) {\n angular.forEach(oldVal, function(val, key) {\n if (newVal[key] !== null && newVal[key] !== oldVal[key]) {\n var component = newVal[key];\n if (component.isCopy) {\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/tools/ledger-importer/scriptlet-copy.html',\n dialogClass: 'xmodal',\n backdropClass: 'modal-backdrop',\n controller: [\"$scope\", \"availableComponents\", function($scope, availableComponents) {\n $scope.availableComponents = availableComponents;\n $scope.formdata = {selected: null};\n }],\n resolve: {\n availableComponents: function() {\n var rv = [];\n angular.forEach($scope.availableComponents[key], function(v) {\n if (!v.isNew) {\n rv.push(v);\n }\n });\n return rv;\n }\n },\n size: 'sm',\n }).result.then(function(clone) {\n if (clone) {\n component = angular.copy(clone);\n component.name = \"Copy of \" + component.name;\n component.id = null;\n component.isNew = true;\n $scope.editComponentAndReload(component).then(function(newComponent) {\n $scope.importScript.components[key].push(newComponent);\n });\n }\n $scope.newComponent[key] = null;\n });\n }\n else if (component.isNew) {\n $scope.editComponentAndReload({code:'', description:'', name:'', script_type: key, isNew: true}).then(function(newComponent) {\n $scope.importScript.components[key].push(newComponent);\n });\n $scope.newComponent[key] = null;\n }\n else {\n $scope.importScript.components[key].push(component);\n $scope.newComponent[key] = null;\n }\n }\n });\n }, true);\n\n $scope.$watch('importScript.primary_vendor_code', function(newVal, oldVal) {\n if (newVal !== oldVal && newVal !== null) {\n importScript.primary_ledger_id = null;\n kwApi.Vendor.getLedgers({id: newVal}).$promise.then(function(ledgers) {\n $scope.ledgers = ledgers;\n });\n }\n });\n\n/* $scope.$watch('importScript.script_type', function(newVal, oldVal) {\n if (newVal !== oldVal && newVal !== null) {\n importScript.import_type = null;\n }\n });*/\n}])\n.controller('StaffLedgerImportScriptletCtrl', [\"$scope\", \"kwApi\", \"importScriptlet\", \"pipelineType\", \"pipelineTypes\", \"kwFileUploadSvc\", \"loading\", \"alertService\", function($scope, kwApi, importScriptlet, pipelineType, pipelineTypes, kwFileUploadSvc, loading, alertService) {\n $scope.importScriptlet = importScriptlet;\n $scope.isUpdate = ($scope.importScriptlet.id ? true : false);\n $scope.pipelineType = pipelineType;\n $scope.pipelineTypes = pipelineTypes;\n $scope.hasParameters = (importScriptlet.parameters && importScriptlet.parameters.length>0 ? \"1\" : \"0\");\n if (!importScriptlet.parameters) importScriptlet.parameters = [];\n\n $scope.uploadCode = function() {\n kwFileUploadSvc.upload({\n title: 'Upload Script',\n description: 'Upload Perl code.',\n instructions: false,\n formdata: {},\n inputs: [],\n url: '/api/import-scriptlet/' + importScriptlet.id + '?op=upload-code'\n }).then(function(rec) {\n angular.extend($scope.importScriptlet, rec.data);\n });\n };\n\n $scope.save = function(data) {\n loading.add();\n var saved = ($scope.isUpdate ? kwApi.ImportScriptlet.put({id: data.id}, data) : kwApi.ImportScriptlet.save({}, data));\n saved.$promise.then(function(newRec) {\n loading.resolve();\n $scope.$close(newRec);\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Save failed');\n });\n };\n\n}])\n\n.controller('StaffLedgerImportScriptRunsCtrl', [\"$scope\", \"$state\", \"importScriptRuns\", \"$uibModal\", \"messageBox\", \"kwApi\", \"alertService\", \"loading\", \"$stateParams\", function($scope, $state, importScriptRuns, $uibModal, messageBox, kwApi, alertService, loading, $stateParams) {\n $scope.runs = importScriptRuns;\n\n $scope.back = function() {\n $state.go('staff.tools.ledger-importer.scripts');\n };\n\n $scope.reload = function() {\n kwApi.ImportScript.getRuns({id: $stateParams.id}).$promise.then(function(data) {\n $scope.runs.replaceWith(data);\n });\n };\n\n $scope.view = function(sels) {\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/tools/ledger-importer/script-execute-results.html',\n dialogClass: 'xmodal',\n backdropClass: 'modal-backdrop',\n backdropClick: false,\n backdrop: 'static',\n size: 'lg',\n controller: [\"$scope\", \"data\", \"import_script_id\", \"$sce\", function($scope,data,import_script_id, $sce) {\n $scope.data = data;\n if ($scope.data.debug) {\n angular.forEach($scope.data.debug, function(d) {\n $sce.trustAsHtml(d.debug);\n });\n }\n if (data.queue_id) {\n $scope.fileDownloadUrl = '/api/import-script/' + import_script_id + '?view=download&qid=' + encodeURIComponent(data.queue_id) + '&as=' + encodeURIComponent(data.export_as);\n }\n else if (data.export_file) {\n $scope.fileDownloadUrl = '/api/import-script/' + import_script_id + '?view=download&file=' + encodeURIComponent(data.export_file) + '&as=' + encodeURIComponent(data.export_as);\n }\n }],\n resolve: {\n data: function() { return sels[0].results; },\n import_script_id: function() { return sels[0].import_script_id; },\n }\n });\n };\n\n $scope.delete = function(sels) {\n messageBox.confirm({\n title: \"Are you sure?\",\n message: \"Are you sure you want to delete \" + (sels.length>1 ? 'these run logs' : 'this run log') + '?'\n }).then(function(result) {\n loading.add();\n var ids = [];\n angular.forEach(sels, function(sel) { ids.push(sel.id); });\n kwApi.ImportScriptRun.forAll({action: 'delete', ids: ids}).$promise.then(function(rv) {\n loading.resolve();\n $scope.reload();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Delete failed');\n });\n });\n };\n}])\n\n.controller('StaffCircHoldsQueueCtrl', [\"$scope\", \"$http\", \"$window\", \"kwApi\", \"userService\", \"loading\", \"$uibModal\", \"alertService\", function ($scope, $http, $window, kwApi, userService, loading, $uibModal, alertService) {\n $scope.showGrid = true;\n $scope.order = {\n field: 'title',\n reverse: false\n }\n\n $scope.selectedBranch = null;\n var headers = { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' };\n\n userService.getAccessibleBranchesAndGroups('circulate.circulate_remaining_permissions').then(function(b) {\n $scope.branches = b.branches;\n $scope.branches.forEach( function (branch) {\n if ( branch.branchcode == userService.login_branch ) {\n $scope.selectedBranch = branch;\n }\n });\n }, function(e){\n console.error(\"Branch list loading error in StaffCircHoldsQueueCtrl: \" + e);\n });\n\n $scope.numSelected = 0;\n\n $scope.viewDetails = function(s) {\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/circ-holds-queue-detail.html',\n windowClass: 'modal',\n controller: [\"$scope\", function($scope) {\n $scope.rec = s[0];\n }],\n });\n };\n\n $scope.gridOptions = {\n enableGridMenu: true,\n enableSorting: true,\n enableColumnResizing: true,\n enableFiltering: true,\n minRowsToShow: 20,\n showGridFooter:true,\n enableFullRowSelection: true,\n exporterPdfDefaultStyle: {fontSize: 9},\n exporterPdfHeader: { text: \" Holds Queue\", style: 'headerStyle' },\n exporterPdfFooter: function ( currentPage, pageCount ) {\n return { text: \" \" + currentPage.toString() + ' of ' + pageCount.toString(), style: 'footerStyle' };\n },\n exporterPdfCustomFormatter: function ( docDefinition ) {\n docDefinition.styles.headerStyle = { fontSize: 22, bold: true, alignment: 'center' };\n docDefinition.styles.footerStyle = { fontSize: 10, bold: true, alignment: 'center' };\n return docDefinition;\n },\n rowTemplate: '
    ',\n columnDefs: [\n { field: 'reservenumber', visible: false },\n { field: 'holdingbranch', displayName: 'Branch' },\n { field: 'title' },\n { field: 'author'},\n { field: 'details' },\n { field: 'ccode', displayName: 'Collection' },\n { field: 'location', displayName: 'Shelving Location' },\n { field: 'itemcallnumber', displayName: 'Call Number' },\n { field: 'enumchron', displayName: 'Enum/Chron' },\n { field: 'barcode_display', displayName: 'Barcode' },\n { field: 'patron' },\n { field: 'notes' },\n { field: 'reservedate', displayName: 'Date', cellFilter: 'dateFmt' },\n { field: 'pickbranch', displayName: 'Send To' },\n ]\n };\n\n $scope.getHolds = function () {\n if ($scope.myGridApi) { $scope.myGridApi.selection.clearSelectedRows(); }\n\n loading.add();\n var selectedBranch = $scope.selectedBranch ? $scope.selectedBranch.branchcode : '';\n $http.get('/api/holds-queue/' + selectedBranch, { authRequired: true }).then(function (rsp) {\n angular.forEach(rsp.data, function(hold){ // Take care of bib details manipulation here.\n\n var details = '';\n\n if (hold.publishercode) {\n details += hold.publishercode;\n }\n\n if (hold.publicationyear) {\n details += '\\n,' + hold.publicationyear;\n }\n else if (hold.copyrightdate) {\n details += '\\n,' + hold.copyrightdate;\n }\n\n if (hold.pages) {\n details += '\\n: ' + hold.pages;\n }\n\n if (hold.size) {\n details += '\\n' + hold.size;\n }\n\n if (hold.isbn) {\n details += 'ISBN: ' + hold.isbn;\n }\n\n hold.details = details;\n hold.patron = hold.surname + ', ' + hold.firstname + ' (' + hold.cardnumber + ')';\n\n if (hold.item_level_request == undefined) {\n hold.barcode_display = 'Selected copy: '+ hold.barcode;\n }\n else {\n hold.barcode_display = hold.barcode;\n }\n\n if (hold.items_out_of_circulation == 1) {\n hold.items_available = 'No';\n }\n else {\n hold.items_available = 'Yes';\n }\n });\n $scope.gridOptions.data = rsp.data ? rsp.data : ''; // undef grid data breaks the display\n $scope.holdsqueue = rsp.data;\n loading.resolve();\n });\n };\n\n $scope.gridOptions.onRegisterApi = function(gridApi) {\n gridApi.core.registerColumnsProcessor(hidePrefColumns);\n\n function hidePrefColumns (columns) {\n var userPrefs = userService.merged_prefs.holds_queue_column;\n for (var property in userPrefs) {\n if (userPrefs.hasOwnProperty(property)) {\n if (userPrefs[property] === false) {\n delete userPrefs[property];\n }\n }\n }\n var rolePrefs = userService.role_prefs.holds_queue_column;\n var yourPrefs = $.extend({}, rolePrefs, userPrefs);\n if (yourPrefs) {\n columns.forEach( function (c, key) {\n if (yourPrefs[c.field]) {\n $scope.gridOptions.columnDefs.forEach( function (def, i) {\n if (def.field === c.field) {\n $scope.gridOptions.columnDefs.splice(i, 1);\n }\n });\n }\n });\n }\n return columns;\n }\n\n $scope.myGridApi = gridApi;\n $scope.myGridApi.selection.on.rowSelectionChanged($scope, function(row) {\n if (row.isSelected) {\n $scope.numSelected++;\n }\n else {\n $scope.numSelected--;\n }\n });\n $scope.myGridApi.selection.on.rowSelectionChangedBatch($scope, function(rows) {\n rows.forEach(function(row) {\n if (row.isSelected) {\n $scope.numSelected++;\n }\n else {\n $scope.numSelected--;\n }\n });\n });\n };\n\n $scope.viewTitle = function () {\n var gridSelection = $scope.myGridApi.selection.getSelectedRows();\n $window.open('/app/staff/bib/' + gridSelection[0].biblionumber + '/details');\n }\n\n $scope.viewPatron = function () {\n var gridSelection = $scope.myGridApi.selection.getSelectedRows();\n window.open('/app/staff/patron/' + gridSelection[0].borrowernumber + '/checkout');\n }\n\n $scope.cancelHolds = function () {\n var gridSelection = $scope.myGridApi.selection.getSelectedRows();\n\n if ( confirm('Confirm the cancellation of selected holds') ) {\n gridSelection.forEach( function (el) {\n $http.post('/api/holds-queue/' + el.biblionumber, $.param( { op: 'cancel', borrowernumber: el.borrowernumber } ),\n { headers: headers, authRequired: true });\n });\n }\n\n $scope.myGridApi.selection.clearSelectedRows();\n $scope.getHolds();\n }\n\n $scope.traceHolds = function () {\n var gridSelection = $scope.myGridApi.selection.getSelectedRows();\n $scope.myGridApi.selection.clearSelectedRows();\n\n gridSelection.forEach( function (el) {\n var params = { op: 'trace', passbranch: el.holdingbranch, reservenumber: el.reservenumber };\n $http.post('/api/holds-queue/' + el.biblionumber, $.param(params), {headers: headers , authRequired: true})\n .then( function () {\n alertService.add({msg: 'Barcode traced: ' + el.barcode, type: 'success'});\n $scope.getHolds();\n }, function (e) {\n console.error(e);\n alertService.add({msg: 'Barcode could not be traced: ' + el.barcode, type: 'error'});\n });\n });\n }\n\n $scope.passHolds = function () {\n var gridSelection = $scope.myGridApi.selection.getSelectedRows(),\n skipped = [];\n\n $scope.myGridApi.selection.clearSelectedRows();\n\n gridSelection.forEach( function (el) {\n if (el.item_level_request) { // Cannot pass on item-level holds.\n skipped.push(el.barcode);\n }\n else {\n var params = { op: 'pass', passbranch: el.holdingbranch, reservenumber: el.reservenumber };\n $http.post('/api/holds-queue/' + el.biblionumber, $.param(params), {headers: headers, authRequired: true})\n .then( function () {\n alertService.add({msg: 'Barcode passed: ' + el.barcode, type: 'success'});\n $scope.getHolds();\n }, function (e) {\n console.error(e);\n alertService.add({msg: 'Barcode could not be passed: ' + el.barcode, type: 'error'});\n });\n }\n });\n\n if (skipped.length > 0) {\n var msg = \"The following barcodes are item level holds and cannot be passed: \" + skipped.join(\", \");\n alert(msg);\n }\n }\n\n $scope.validateBarcode = function () {\n var rows = $scope.myGridApi.selection.getSelectedRows(),\n row = rows[0];\n\n $scope.myGridApi.selection.clearSelectedRows();\n\n if ( row.barcode == \"Any available copy\" ) {\n var modal = $uibModal.open({\n backdrop: true,\n templateUrl: '/app/static/partials/staff/circ/hold-barcode-checkin-modal.html',\n controller: [\"$scope\", \"$uibModalInstance\", function ($scope, $uibModalInstance) {\n $scope.userBarcode = '';\n $scope.barcodeSubmit = function (barcode) {\n $uibModalInstance.close(barcode);\n }\n }]\n });\n\n modal.result.then( function (rv) {\n return rv ? $scope.checkInHold(rv, row.biblionumber, row.reservenumber) : false;\n });\n }\n else {\n $scope.checkInHold(row.barcode, row.biblionumber, row.reservenumber);\n }\n\n }\n\n $scope.checkInHold = function (barcode, biblionumber, reservenumber) {\n $window.open('/app/staff/circ/checkin/' + barcode);\n\n var params = { op: 'checkIn', reservenumber: reservenumber };\n $http.post('/api/holds-queue/' + biblionumber, $.param(params), {headers: headers, authRequired: true})\n .then( function () {\n alertService.add({msg: 'Hold checked in', type: 'success'});\n $scope.getHolds();\n }, function (e) {\n console.error(e);\n alertService.addApiError(e,'Hold could not be checked in');\n });\n }\n\n $scope.selectAll = function() {\n $scope.myGridApi.selection.selectAllRows();\n }\n\n $scope.clearAll = function() {\n $scope.myGridApi.selection.clearSelectedRows();\n }\n\n $scope.getHolds();\n}])\n\n.directive('kwPatronSelect', [\"$http\", \"$state\", \"configService\", \"$timeout\", \"userService\", function($http, $state, configService, $timeout, userService) {\n // typeahead or barcode search for patron.\n // use: \n // shows error if patron not found, otherwise triggers state change.\n // or: \n // note in this form it may still trigger a patron search if circAutoCompl is off . [FIXME]\n // optional attributes:\n // require-select => no search expansion; must select from list.\n\n return {\n templateUrl: '/app/static/partials/staff/patron-quick-select.html',\n scope: { targetState: '@', // static value.\n onSelect: '&'\n },\n link: function(scope, elm, attrs, ngModelCtrl) {\n // $timeout(function(){\n // elm.find('input').first().focus();\n // },2);\n // Note ui-bootstrap's modal steals focus unless\n // an input has autofocus attribute.\n\n scope.withSort = 'withSort' in attrs;\n scope.requireSelect = ('requireSelect' in attrs);\n\n scope.autocomplete = (scope.requireSelect || attrs.onSelect) || configService.CircAutocompl;\n\n scope.psearch = {\n //query value will become patron id if search is successful.\n query: '',\n shadowQuery: '',\n noResults: false,\n orderby: '', // default is name\n loading: false\n };\n\n scope.clearSearch = function(){\n\n scope.psearch.query = scope.psearch.shadowQuery = '';\n $timeout(function(){\n scope.psearch.loading = scope.psearch.noResults = false;\n // manually setting since ui-bootstrap 0.13 typeahead\n // remains open after auto-select on barcode match.\n });\n };\n\n scope.findPatron = function(){\n if(!scope.requireSelect){\n scope.expandPatronSearch();\n }\n };\n\n scope.go = function(patron){\n scope.clearSearch();\n if(attrs.targetState){\n var rs = $state.go(attrs.targetState, { borrowernumber: patron.id }, { reload: true , inherit: false });\n } else if(scope.onSelect){\n scope.onSelect({ '$id': patron.id });\n }\n };\n\n scope.expandPatronSearch = function(q){\n var query = (scope.autocomplete) ? scope.psearch.shadowQuery : scope.psearch.query;\n $state.go('staff.patrons.search', { member: query }, { reload: false });\n scope.clearSearch();\n };\n\n var nameRegex = /([a-zA-Z-]+)\\s*(,?)\\s*([a-zA-Z-]+)?/; // this should really be done on backend.\n\n scope.searchPatrons = function(val, maxResults) {\n scope.psearch.shadowQuery = val;\n var params = {\n cache: true,\n view: 'picker',\n limitQuery: maxResults || 25,\n branchcode: userService.login_branch,\n orderBy: scope.psearch.orderby\n };\n\n var m = nameRegex.exec(val);\n if(m && m[3]){\n params.searchBoth = true;\n if(m[2]==','){\n params.searchFirst = m[3];\n params.searchLast = m[1];\n } else {\n params.searchFirst = m[1];\n params.searchLast = m[3];\n }\n } else {\n params.searchValue = val;\n }\n\n return $http.get('/api/patron', { 'params': params }).then( function (response) {\n var responses = response.data;\n responses = responses.map( function (d) {\n return {\n id: d.id,\n label: d.name + ' (' + d.card_number + ') -- ' + d.id + ' ' + d.branch_code,\n cardnumber: d.card_number\n };\n });\n // auto-match cardnumber:\n if(responses.length==1 && val == responses[0].cardnumber){\n scope.go( responses[0] );\n return []; // otherwise dropdown doesn't close.\n\n }\n // note typeahead-select-on-exact tests that the $viewValue matches the label returned above.\n\n return responses;\n });\n };\n }\n };\n}])\n\n.controller('StaffEventLogCtrl', [\"$scope\", \"$timeout\", \"$http\", \"$uibModal\", \"kwApi\", \"loading\", \"alertService\", \"userService\", \"$sce\", \"fieldMap\", \"$filter\", function ($scope, $timeout, $http, $uibModal, kwApi, loading, alertService, userService, $sce, fieldMap, $filter) {\n $scope.fieldMap = fieldMap;\n\n var moduleName = {\n 'ACQLIST': 'Infomart',\n 'CATALOG': 'Catalog access',\n 'CATALOGUING': 'Cataloging',\n 'AUTHORITIES': 'Authorities',\n 'CIRCULATION': 'Circulation',\n 'MEMBERS': 'User',\n 'SOLRSYNC': 'SOLR Sync',\n 'SERIAL': 'Serials',\n 'SYSTEMPREFERENCE': 'System Config'\n };\n\n var transformObject = function(e) {\n if (moduleName[e.module])\n e.module = moduleName[e.module];\n\n if (!e.link && e.object) {\n var id = e.object;\n var name;\n switch (e.object_type) {\n case 'ItemList':\n name = e.list_name ? e.list_name : \"(List \" + id + \")\";\n e.link = \"\" + name + \"\";\n break;\n case 'ImportBatch':\n e.link = \"Batch \" + id + \"\";\n break;\n case 'Bib':\n e.link = \"Bib \" + id + \"\";\n break;\n case 'Item':\n if (e.bib_id) {\n //e.link = \"Item \" + id + \" in Bib \" + e.bib_id + \"\";\n e.link = \"Item \" + id + \" in Bib \" + e.bib_id + \"\";\n }\n break;\n case 'Subscription':\n e.link = \"Subscription \" + id + \"\";\n break;\n case 'Issue':\n if (e.bib_id) {\n //e.link = \"Issue \" + id + \" in Bib \" + e.bib_id + \"\";\n e.link = \"Issue \" + id + \" in Bib \" + e.bib_id + \"\";\n }\n break;\n case 'VendorBranch':\n e.link = \"\" + e.object + \"\";\n break;\n case 'Ledger':\n if (e.vendor_id)\n e.link = \"\" + e.object + \"\";\n break;\n default:\n e.link = id;\n break;\n }\n }\n\n if (e.link)\n $sce.trustAsHtml(''+e.link);\n\n if (e.member_name) {\n e.member_name = \"\" + e.member_name + \"\";\n }\n else if (e.user === \"0\") {\n e.member_name = 'System User';\n }\n else if (e.user === \"-1\") {\n e.member_name = 'Anonymous User';\n }\n else if (e.user) {\n e.member_name = \"Member #\" + e.user + \"\";\n }\n if (e.member_name)\n $sce.trustAsHtml(e.patron_name);\n \n };\n\n if (!($scope.limits = userService.getPref('staff.event_log.limits'))) {\n $scope.limits = {\n module: undefined,\n since: '1 day',\n start_date: undefined,\n end_date: undefined,\n mode: 'logs',\n y1: 'All',\n dummy: 0\n };\n userService.setPref('staff.event_log.limits', $scope.limits);\n $scope.limits.since = '1 day';\n }\n\n $scope.switchMode = function(mode) {\n $scope.limits.mode = mode;\n };\n\n $scope.groupBy = function(group) {\n $scope.limits.y1 = group;\n };\n\n $scope.viewModule = function(module) {\n if (module === '') \n $scope.limits.module = undefined;\n else\n $scope.limits.module = module;\n };\n\n $scope.viewSince = function(since) {\n if (since === '') \n $scope.limits.since = undefined;\n else\n $scope.limits.since = since;\n $scope.limits.start_date = undefined;\n $scope.limits.end_date = undefined;\n };\n\n $scope.calendar = {\n opened: {\n 'start_date': false,\n 'end_date': false,\n },\n dateFormat: 'yyyy-MM-dd',\n open: function(which) {\n if ($scope.limits.start_date === undefined)\n $scope.limits.start_date = new Date();\n if ($scope.limits.end_date === undefined)\n $scope.limits.end_date = new Date();\n\n $scope.limits.since = undefined;\n\n $timeout(function(){\n $scope.calendar.opened[which] = true;\n });\n }\n };\n\n $scope.$watch('limits', function(newVal, oldVal) {\n if (newVal && oldVal) {\n userService.setPref('staff.event_log.limits', $scope.limits);\n }\n\n if (newVal) {\n $scope.settingsDescription = \"Viewing \";\n var params = {};\n params.view = newVal.mode;\n if (newVal.module) {\n params.module = newVal.module;\n $scope.settingsDescription += moduleName[params.module];\n }\n \n if (params.view == 'stats') {\n $scope.settingsDescription += \" statistics derived from activity\";\n }\n else {\n $scope.settingsDescription += \" activity\";\n }\n\n if (newVal.since) {\n params.since = newVal.since;\n $scope.settingsDescription += \" since \" + params.since + \" ago\";\n }\n if (newVal.start_date) {\n params.start_date = dateFormat(newVal.start_date);\n $scope.settingsDescription += \" from \" + $filter('kohaDate')(params.start_date);\n }\n if (newVal.end_date) {\n params.end_date = dateFormat(newVal.end_date);\n $scope.settingsDescription += \" to \" + $filter('kohaDate')(params.end_date)\n }\n if (newVal.y1 && (params.view == 'stats')) {\n params.y1 = newVal.y1;\n if (params.y1 != 'All')\n $scope.settingsDescription += \" grouped by \" + params.y1;\n }\n if (params.view == 'stats') {\n $scope.stats(params);\n }\n else {\n $scope.load(params);\n }\n\n }\n }, true);\n\n //$scope.deleteBefore = $scope.deleteBefore.setDate($scope.deleteBefore.getDate() - 30);\n\n var dateFormat = function (date) {\n var d = new Date(date);\n var curr_date = d.getDate();\n if (curr_date < 10)\n curr_date = \"0\" + curr_date;\n var curr_month = d.getMonth() + 1;\n if (curr_month < 10)\n curr_month = \"0\" + curr_month;\n var curr_year = d.getFullYear();\n var date_string = \"\" + curr_year + \"-\" + curr_month + \"-\" + curr_date;\n return date_string;\n }\n\n $scope.deleteEventLog = function () {\n var result = confirm(\"This action cannot be undone. Confirm deletion:\");\n var date_string = dateFormat($scope.deleteBefore);\n if (result)\n return $http({method: 'DELETE', url: '/api/event-log?date=' + date_string, authRequired: true});\n }\n\n $scope.logs = [];\n $scope.load = function (params) {\n loading.add();\n kwApi.ActionLog.getList(params).$promise.then(function(data) {\n angular.forEach(data, function(row) {\n transformObject(row);\n });\n loading.resolve();\n\n $scope.logs.replaceWith(data);\n }, function(err) {\n alertService.addApiError(err,'Load error');\n loading.resolve();\n });\n };\n \n $scope.statsGrid = {\n data: [],\n columnDefs: [],\n //enableFiltering: true,\n enableSorting: true,\n flatEntityAccess: true,\n enableColumnResizing: true,\n enablePagination: false,\n enableColumnMenus: true,\n enableRowSelection: false,\n enableRowHeaderSelection: false,\n showGridFooter: true,\n enableFooterTotalSelected: false,\n multiSelect: false,\n enableGridMenu: true,\n };\n \n $scope.stats = function (params) {\n loading.add();\n kwApi.ActionLog.getStats(params).$promise.then(function(rv) {\n console.dir(rv);\n $scope.statsGrid.data.length = 0;\n angular.forEach(rv.data, function(row) {\n var obj = {};\n for (var i=0; i= 0; i--) {\n if(accttype.id == $scope.accounttypes[i].id) $scope.accounttypes.splice(i,1);\n }\n alertService.add({msg: \"Invoice type deleted.\", type: \"success\"});\n\n }, function(fail){\n console.warn(fail);\n alertService.add({msg: \"Failed to delete Invoice type. \" + fail.data.replace(/\\d\\d\\d\\s*/,''), type: \"error\"});\n\n });\n };\n}])\n\n.controller('StaffPatronAccountingCtrl', [\"$scope\", \"$state\", \"$http\", \"$uibModal\", \"$q\", \"kwApi\", \"alertService\", \"configService\", \"kohaDlg\", \"userService\", \"loading\", \"$sce\", function ($scope, $state, $http, $uibModal, $q, kwApi, alertService,\n configService, kohaDlg, userService, loading, $sce) {\n\n var patronid = $state.params.borrowernumber;\n\n $scope.acct = {\n accruing_balance: null,\n balance: null, // Note this accounts for ExcludeAccruingInTotal\n net_credit: false,\n };\n\n $scope.txns = {\n show: { pmt: true, fee: 'current' }, // TODO: add param to routing so we can link to history\n view: [],\n fee: {},\n pmt: {},\n accruing: [],\n selection: function(){\n return $scope.txns.view.filter(function(txn){ return txn._selected; });\n },\n selectAll: function(){\n var toggle = !$scope.txns.selection().length;\n angular.forEach($scope.txns.fee, function(fee){\n fee._selected = (fee.outstanding && !fee.noncreditable) ? toggle : false;\n });\n }\n };\n var updateView = function(){\n\n // TODO: we may wish to prevent user from hiding everything ?\n var merged = [];\n if ($scope.txns.show.fee){\n angular.forEach($scope.txns.fee, function(fee){\n if($scope.txns.show.fee!='current' || fee.outstanding)\n merged.push(fee);\n });\n merged = merged.concat($scope.txns.accruing);\n }\n\n\n function isPmtLinked (pmt, fees){\n return !! fees.filter(function(fee){\n return (fee.credits||[]).filter(function(credit){\n return credit.payment_id == pmt.id;\n }).length;\n }).length;\n }\n // payments must be date-filtered if not showing history .\n if($scope.txns.show.pmt){\n var pmtsShown = [];\n\n if($scope.txns.show.fee == 'current'){\n pmtsShown = Object.values($scope.txns.pmt).filter(function(pmt){\n return !!pmt.unallocated || isPmtLinked(pmt, merged) ||\n dayjs().isSame(pmt.date, 'day');\n }); // Object.values dne in IE.\n } else {\n pmtsShown = Object.values($scope.txns.pmt);\n }\n\n merged = merged.concat(pmtsShown);\n }\n $scope.txns.view = merged;\n };\n $scope.$watchCollection('txns.show', updateView);\n\n var loadAllTxns = function(){\n loading.add('refreshAll');\n $scope.hasRefundsToDo = false;\n var feesPromise = kwApi.Fee.getForPatron({ patron_id: patronid }, function(fees){\n fees.forEach(function(fee){ $scope.txns.fee[fee.id] = fee; });\n }).$promise;\n var paymentsPromise = kwApi.Payment.getForPatron({ patron_id: patronid }, function(pmts){\n pmts.forEach(function(pmt){\n $scope.txns.pmt[pmt.id] = pmt;\n if(pmt.unallocated && !pmt.reallocate) $scope.hasRefundsToDo = true;\n\n });\n }).$promise;\n $scope.txns.accruing = kwApi.Fee.getForPatron({ patron_id: patronid, type: 'accruing' });\n $q.all( [ $scope.txns.accruing.$promise, feesPromise, paymentsPromise ]).then(function(){\n loading.resolve();\n updateView();\n loading.resolve('refreshAll');\n });\n };\n\n var updateSummary = function(){\n $http.get('/api/patron/'+patronid+'/account-summary').then(function(rsp){\n $scope.acct.balance = rsp.data.balance; // #includes accruing depending on syspref.\n $scope.acct.accruing_balance = rsp.data.accruing_balance;\n $scope.acct.net_credit = ($scope.acct.balance < 0);\n $scope.acct.patron_alerted = rsp.data.last_fines_alert;\n $scope.acct.fines_alert_amt = rsp.data.fines_alert_threshold || 999999;\n $scope.acct.showPatronAlert =\n ( $scope.acct.balance>0 && userService.can({fees: 'send_alert'})) ||\n $scope.acct.patron_alerted || ( $scope.acct.balance > $scope.acct.fines_alert_amt );\n });\n };\n\n// Redistribute credits on view load.\n// Likely not the ideal way to handle this, but (arguably) better than\n// all payments on the backend triggering changes to other fees/payments.\n function redistributeCredits(){\n loading.add('redistribute');\n return kwApi.PatronAccount.redistributeCredits({id: patronid}).$promise.then(function(rsp){\n updateSummary();\n loadAllTxns();\n loading.resolve('redistribute');\n }, function(f){\n updateSummary();\n loadAllTxns();\n console.warn(f);\n loading.resolve('redistribute');\n });\n }\n redistributeCredits();\n\n var credit_action = (userService.can({ fees: 'accept_payment'})) ?\n { PAYMENT: 'Pay' , CREDIT: 'Credit' } : {};\n\n if(userService.can({fees: 'transfer'})) credit_action.TRANSBUS = \"Transfer\";\n\n if(userService.can({fees: 'waive'})){\n credit_action.FORGIVE = 'Forgive';\n credit_action.WRITEOFF = 'Write off';\n }\n $scope.creditActions = ['PAYMENT','CREDIT','TRANSBUS','FORGIVE','WRITEOFF'].filter(function(t){return credit_action[t];});\n $scope.creditTypes = {};\n\n $scope.user_can_credit = Object.keys(credit_action).length;\n\n $scope.feeTypes = {};\n $scope.creditTypes = {};\n $scope.acctLabel = { '_ACCRUING': 'Accruing Overdue' };\n kwApi.AccountType.getList({},function(ats){\n ats.forEach(function(accttype){\n if(accttype.class=='fee'||accttype.class=='invoice'){\n $scope.feeTypes[accttype.accounttype] = accttype;\n } else {\n if(credit_action[accttype.accounttype]){\n accttype.verb = credit_action[accttype.accounttype];\n $scope.creditTypes[accttype.accounttype] = accttype;\n }\n }\n $scope.acctLabel[accttype.accounttype] = accttype.description;\n });\n });\n\n var syncFees = function( affectedFees ){\n// we should probably block ui on any updates.\n\n (affectedFees||[]).forEach(function(fee){\n fee.$get();\n });\n };\n\n $scope.refreshCurrent = function(){\n // reloads all payments with unallocated credits and unpaid fees.\n // this should be sufficient for most transactions.\n loading.add('refreshCurrent');\n var blocking = [];\n angular.forEach($scope.txns.fee, function(fee){\n if(fee.outstanding)\n blocking.push( fee.$get().$promise );\n });\n angular.forEach($scope.txns.pmt, function(pmt){\n if(pmt.unallocated)\n blocking.push( pmt.$get().$promise );\n });\n $q.all(blocking).then(function(){\n loading.resolve('refreshCurrent');\n updateView();\n });\n };\n\n $scope.sort = { field: 'timestamp', reverse: true };\n\n $scope.payOneFee = function(fee, accttype){\n if($scope.txns.selection().length) $scope.txns.selectAll();\n fee._selected = true;\n $scope.openCreditModal(accttype);\n };\n\n $scope.openCreditModal = function(accttype) {\n var parentScope = $scope;\n if(!accttype) accttype = 'PAYMENT';\n if(!$scope.txns.selection().length && $scope.creditTypes[accttype].class!='payment'){\n // cannot waive without selection. must explicitly select all first.\n return;\n }\n $uibModal.open({\n title: 'Add Credit',\n templateUrl: '/app/static/partials/staff/patrons/add-credit-modal.html',\n // size: 'sm',\n backdrop: false, // FIXME: Allow user to interact with non-modal elements\n // so that target fees can be changed/reordered.\n backgroundClick: false,\n controller: [\"$scope\", \"$q\", function($scope, $q) {\n // loading.resolve();\n\n $scope.$watchCollection(parentScope.txns.selection, function(txns){\n if(!txns) return;\n $scope.multiTarget = txns.length > 1;\n $scope.targetFees = txns;\n if(txns.length){\n $scope.targetFeeTotal = txns.reduce(function(acc,cur){\n return cur.outstanding + acc; }, 0);\n } else {\n // pay all.\n $scope.targetFeeTotal = parentScope.acct.balance;\n if (!configService.ExcludeAccruingInTotal) $scope.targetFeeTotal -= parentScope.acct.accruing_balance;\n }\n });\n\n $scope.accttype = accttype || 'PAYMENT';\n $scope.creditVerb = parentScope.creditTypes[$scope.accttype].verb;\n $scope.balance = parentScope.acct.balance;\n if(!configService.ExcludeAccruingInTotal)\n $scope.assessed_balance = $scope.balance - parentScope.acct.accruing_balance;\n $scope.isPayment = parentScope.creditTypes[accttype].class=='payment';\n $scope.acctLabel = parentScope.acctLabel;\n $scope.credit = {\n description: '',\n accounttype: $scope.accttype,\n amount: undefined,\n borrowernumber: patronid,\n branchcode: userService.login_branch\n };\n\n var updateAffected = function(fees){\n updateSummary();\n syncFees( fees || $scope.targetFees );\n };\n\n $scope.addCredit = function(){\n if(parentScope.creditTypes[$scope.accttype].class=='payment'){\n // reload affected fees (or all fees)\n loading.add('pmt');\n var credit = angular.copy($scope.credit);\n credit.target_fees = $scope.targetFees.map(function(fee){ return fee.id; });\n\n if(!credit.amount && !credit.target_fees.length){\n // pay full balance. Don't pay accruing.\n var to_credit = $scope.balance;\n if (!configService.ExcludeAccruingInTotal) to_credit -= parentScope.acct.accruing_balance;\n if(to_credit > 0){\n credit.amount = to_credit;\n } else {\n return; // should probably alert.\n }\n }\n var pmt = kwApi.Payment.create( credit , function(p){\n console.log(p);\n parentScope.refreshCurrent();\n parentScope.txns.pmt[pmt.id] = pmt;\n updateSummary();\n updateView();\n // FIXME: paying fee does not remove it from view if in current fees view.\n loading.resolve('pmt');\n $scope.$close();\n }, function(fail){\n console.warn(fail);\n loading.resolve('pmt');\n });\n\n } else {\n // waive.\n loading.add('waive');\n\n var amt = $scope.credit.amount;\n // todo: allow per-fee amounts.\n var promises = [];\n var fees;\n if($scope.targetFees.length){\n fees = $scope.targetFees;\n } else {\n // FIXME: Do we really want to waive all fees ?\n // (No, this is prevented).\n // fees = parentScope.txns.fee.filter(function(fee){\n // return fee.outstanding && !fee.noncreditable;\n // });\n }\n fees.forEach(function(fee){\n var to_waive = ($scope.credit.amount && (fee.outstanding > $scope.credit.amount) ) ?\n $scope.credit.amount : undefined;\n var credit = angular.extend({}, $scope.credit, { amount: to_waive });\n credit.notes = credit.description;\n\n var req = fee.$waive( credit, function(fee_updated){\n console.log(fee_updated);\n } );\n promises.push(req);\n });\n $q.all(promises).then(function(rv){\n loading.resolve('waive');\n updateAffected(fees);\n }, function(err){\n console.warn(err);\n loading.resolve('waive');\n });\n }\n $scope.$close();\n };\n }]\n });\n };\n\n $scope.openInvoiceModal = function() {\n var parentScope = $scope;\n var feeTypes = $scope.feeTypes;\n $uibModal.open({\n title: 'Add Manual Invoice',\n templateUrl: '/app/static/partials/staff/patrons/add-fee-modal.html',\n // size: 'sm',\n backgroundClick: false,\n\n controller: [\"$scope\", \"$q\", function($scope, $q) {\n // loading.resolve();\n $scope.fee = {\n description: '',\n accounttype: $scope.accttype,\n amount: undefined,\n borrowernumber: patronid,\n branchcode: userService.login_branch,\n };\n $scope.linked_item = {\n item: null,\n barcode: null\n };\n\n $scope.$watch( 'fee.accounttype', function(accttype){\n if(accttype && feeTypes[accttype].data.default_amount){\n $scope.fee.amount = feeTypes[accttype].data.default_amount;\n }\n });\n $scope.invoiceTypes = Object.keys(feeTypes).reduce(function(filtered, code){\n if(feeTypes[code].data.invoice)\n filtered.push( angular.extend(feeTypes[code],\n { class_desc: (feeTypes[code].class=='fee') ? 'System Fees' : 'Local Invoice Types' })\n );\n return filtered;\n }, []).sort(function(a,b){ return (a.description.toLowerCase() > b.description.toLowerCase()) ? 1 : -1;});\n\n $scope.addFee = function(){\n if(!parseFloat($scope.fee.amount)){\n return;\n }\n loading.add('addFee');\n if($scope.linked_item.item){\n $scope.fee.itemnumber = $scope.linked_item.item.itemnumber;\n }\n kwApi.Fee.create( $scope.fee , function(f){\n parentScope.txns.fee[f.id] = f;\n if(parentScope.acct.unallocated_amount)\n redistributeCredits();\n else {\n updateSummary();\n updateView();\n }\n loading.resolve('addFee');\n $scope.$close();\n }, function(fail){\n console.warn(fail);\n loading.resolve('addFee');\n alertService.add({msg: \"Add Invoice failed: \" + fail.data.replace(/\\d\\d\\d\\s*/,''), type: \"error\"});\n });\n };\n }]\n });\n };\n\n $scope.openRefundsModal = function( payment ) {\n var refunds = [];\n if(payment){\n refunds.push(payment);\n } else {\n angular.forEach($scope.txns.pmt, function(pmt){\n if(pmt.unallocated && !pmt.reallocate) refunds.push( pmt );\n });\n }\n\n var parentScope = $scope;\n var acctLabel = $scope.acctLabel;\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/patrons/refunds-modal.html',\n // size: 'sm',\n // Refunds are limited to lost item fees.\n // Currently [2019-04], they're only triggered if the syspref\n // RefundLostReturnedAmount is set.\n\n controller: [\"$scope\", \"$q\", function($scope, $q) {\n $scope.refunds = refunds;\n $scope.localRefunds = configService.RefundLostReturnedAmount;\n $scope.selected = {};\n refunds.forEach(function(pmt){\n console.log(pmt);\n console.log(userService.login_branch);\n if(pmt.data.fee_id){\n /// find the item the payment was meant for.\n // we need its owning branch.\n pmt.oldFee = parentScope.txns.fee[pmt.data.fee_id];\n if(pmt.oldFee.itemnumber){\n $http.get('/api/item/'+pmt.oldFee.itemnumber, {cache: true}).then(function(rsp){\n var item = rsp.data;\n if(item.homebranch==userService.login_branch)\n $scope.selected[pmt.id] = true;\n });\n } else {\n if(pmt.branchcode==userService.login_branch)\n $scope.selected[pmt.id] = true;\n }\n }\n });\n $scope.selectAll = function(){\n if(Object.keys($scope.selected).some(function(id){ return $scope.selected[id]; })){\n refunds.forEach(function(pmt){ $scope.selected[pmt.id] = false; });\n } else {\n refunds.forEach(function(pmt){ $scope.selected[pmt.id] = true; });\n }\n };\n $scope.acctLabel = acctLabel;\n $scope.refundTotal = function(){\n return refunds.reduce(function(acc,pmt){\n return ($scope.selected[pmt.id]) ? acc - pmt.unallocated : acc;\n }, 0);\n };\n\n // currently reallocate flag determines refundability.\n $scope.setReallocate = function(){\n // FIXME: Allow Undo?\n kohaDlg.dialog({\n // heading: \"Confirm\",\n message: \"Unlink payment from lost item fees, and apply [ $\" + $scope.refundTotal() +\n \" ] to remaining (or future) patron account fees?\"\n }).result.then(function(ok) {\n $scope.$close();\n if(!ok) return;\n loading.add('setReallocate');\n var promises = [];\n refunds.forEach(function(pmt){\n if(!$scope.selected[pmt.id] || pmt.reallocate)\n return;\n pmt.reallocate = true;\n var p = pmt.$setReallocate();\n promises.push(p.$promise);\n });\n $q.all(promises).then(function(){\n loading.resolve('setReallocate');\n redistributeCredits();\n }, function(err){\n loading.resolve('setReallocate');\n });\n });\n };\n $scope.doRefunds = function(){\n loading.add('refundPayment');\n var promises = [];\n refunds.forEach(function(pmt){\n if(!$scope.selected[pmt.id] || pmt.reallocate)\n return;\n var p = pmt.$reverse({accounttype: 'REFUND'});\n promises.push(p.$promise);\n });\n $q.all(promises).then(function(){\n loading.resolve('refundPayment');\n redistributeCredits();\n }, function(err){\n loading.resolve('refundPayment');\n });\n $scope.$close();\n };\n }]\n });\n };\n\n $scope.sendAccountingModal = function() {\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/patrons/account-receipt-modal.html',\n controller: [\"$scope\", function($scope) {\n $scope.borrowernumber = patronid;\n kwApi.PatronAccount.getReceipt({id: patronid}).$promise.then(function(rsp){\n $scope.receipt = $sce.trustAsHtml(rsp.data);\n }, function(fail){\n console.warn(fail);\n alertService.addApiError(fail,'Failed to get account receipt');\n });\n $scope.emailReceipt = function(){\n kwApi.PatronAccount.emailReceipt({id: patronid}).$promise.then(function(rsp){\n alertService.add({msg: \"Account receipt sent\", type: \"success\"});\n }, function(fail){\n console.warn(fail);\n alertService.addApiError(fail,'Message send failed');\n });\n };\n }]\n });\n };\n\n $scope.reversePmt = function(pmt, accttype){\n\n var amtStr = '$' + pmt.amount.toFixed(2);\n var heading, message;\n var verb = {\n // REFUND: 'Apply Refund',\n REVERSED_PAYMENT: 'Reverse Payment',\n CANCELCREDIT: 'Cancel Credit'\n };\n if(!accttype) accttype = (pmt.accounttype=='CREDIT') ? 'CANCELCREDIT' : 'REVERSED_PAYMENT';\n\n kohaDlg.dialog({\n heading: verb[accttype] +' ' + amtStr,\n message: verb[accttype] + ' of ' + amtStr + ' to patron account ?',\n buttons: [{val: true, label: verb[accttype], btnClass: 'btn-primary'},\n {val: false, label: 'No'}],\n }).result.then(function(rv) {\n if (rv) {\n loading.add('reversePmt');\n pmt.$reverse({ accounttype: accttype }, function(){\n loading.resolve('reversePmt');\n // really all we need to do is reload fees but...\n loadAllTxns();\n updateSummary();\n }, function(e){ loading.resolve('reversePmt');});\n }\n });\n };\n\n $scope.canReverse = function(pmt){\n return userService.can({fees: 'create'}) && !pmt.reversed;\n };\n $scope.pmtReversedDisplay = {\n CANCELCREDIT: 'Credit Canceled',\n REVERSED_PAYMENT: 'Payment Reversed',\n REFUND: 'Refunded'\n };\n\n $scope.can_alert = userService.can({fees: 'send_alert'});\n $scope.sendFinesAlert = function(){\n var msg = \"Send email fines alert ? \";\n if($scope.acct.fines_alert_amt){\n msg += \"The patron receives this alert automatically when fines exceed \" + $scope.acct.fines_alert_amt.toFixed(2);\n }\n kohaDlg.dialog({ message: msg }).result.then(function(rv) {\n if (rv) {\n // send alert...\n // $http.post('/api/patron/'+patronid+'/account-summary', { op: 'send_alert' }).then(\n kwApi.PatronAccount.sendAlert({id: patronid}).$promise.then(\n function(rsp){\n updateSummary();\n }, function(fail){\n console.warn(fail);\n alertService.addApiError(fail,'Message send failed');\n });\n }\n });\n };\n $scope.creditDetailTmpl = '/app/static/partials/components/credit-detail-popover.html';\n}])\n\n.directive('bvAcctTxnDetail', [\"kwApi\", \"$http\", \"configService\", function(kwApi, $http, configService) {\n return {\n scope: {\n txn: \"=bvAcctTxnDetail\",\n // attrs:\n // brief (bool) : don't fetch additional bib / issue data.\n //\n },\n templateUrl: '/app/static/partials/staff/patrons/acct-txn-detail.html',\n link: function(scope, el, attrs){\n // Display of FEE:\n // lost item fees\n // accruing fees\n // overdues\n // still to do: reserve fees, etc\n\n var brief = 'brief' in attrs;\n if(brief) el.addClass('brief');\n\n scope.item = {itemnumber: undefined, biblionumber: undefined};\n scope.$watch('txn', function(txn){\n if(!txn) return;\n\n scope.title = brief ? txn.briefTitle : txn.title;\n if (txn.owningBranch) scope.owningBranch =\n configService.interpolator('bv-branch').display(txn.owningBranch);\n if (txn.itemnumber) {\n scope.item.itemnumber = txn.itemnumber;\n scope.item.biblionumber = txn.itemBib;\n scope.item.barcode = txn.barcode;\n }\n if(!scope.item.biblionumber && txn.itemnumber){ // accruing fees are missing this datum\n $http.get('/api/item/' + txn.itemnumber, {cache: true}).then(function(rsp){\n scope.item = rsp.data;\n scope.owningBranch = configService.interpolator('bv-branch').display(scope.item.homebranch);\n if (!scope.title) {\n if(brief){\n scope.title = scope.item.bib_title;\n } else {\n kwApi.Work.get({id: scope.item.biblionumber}, function(bib){\n scope.bib = bib;\n scope.title = bib.title_ext;\n });\n }\n }\n });\n }\n if(txn.duedate){ // i.e. accruingfee.\n scope.issue = { duedate: txn.duedate };\n }\n if(! brief){\n if(txn.accounttype=='FINE' && txn.object){\n scope.issue = kwApi.Issue.get({id: txn.object});\n }\n }\n\n // FIXME: Add cached model layer.\n });\n\n }\n };\n}])\n.directive('kwItemBarcodeLookup', [\"kwApi\", \"$timeout\", function(kwApi, $timeout) {\n return {\n require: 'ngModel',\n scope: {\n item: '=kwItemBarcodeLookup'\n },\n link: function(scope, elm, attrs, ctrl) {\n var nextInput = elm.closest('.form-group').next().find('input, button').first();\n // var jj = elm.wrap('
    ');\n // console.log(jj);\n\n var lookup = function(bc) {\n kwApi.Item.getByBarcode({ barcode: bc }, function(item){\n scope.item = item;\n scope.item.title = '...';\n kwApi.Work.get({id: item.biblionumber}, function(bib){\n item.title = bib.title;\n });\n ctrl.$setValidity('item', true);\n }, function(err){\n ctrl.$setValidity('item', false);\n scope.item = null;\n });\n };\n elm.blur(function(evt){\n if(ctrl.$viewValue){\n lookup(ctrl.$viewValue);\n } else {\n $timeout(function(){\n ctrl.$setValidity('item', true);\n });\n }\n });\n elm.keypress(function(evt){\n if (evt.which == 13) {\n console.warn(evt);\n nextInput.focus();\n evt.preventDefault();\n } else {\n // $timeout(function(){\n scope.item = null;\n // });\n\n }\n });\n }\n };\n}])\n\n.controller('StaffPatronGroupCtrl', [\"$scope\", \"$state\", \"kwApi\", \"alertService\", \"kohaDlg\", \"$injector\", \"$q\", \"$http\", \"userService\", \"configService\", function ($scope, $state, kwApi, alertService,\n kohaDlg, $injector, $q, $http, userService, configService) {\n\n $scope.loading = true;\n var patronid = $state.params.borrowernumber;\n\n $scope.patron_categories = Object.keys(configService.patroncats)\n .map(function(p) { return { value: p, display: configService.patroncats[p].description }; })\n .sort(function(a,b) { return (a.display < b.display ? -1 : a.display > b.display ? 1 : 0) });\n\n var renderMembers = function () {\n kwApi.PatronGroup.getGroup({id: patronid}).$promise.then(function(rv) {\n $scope.members = rv;\n $scope.total_holds = 0;\n $scope.total_issues = 0;\n $scope.total_overdue = 0;\n $scope.total_fines = 0;\n rv.forEach(function(m){\n $scope.total_holds += Number(m.holds_summary.total);\n $scope.total_issues += Number(m.issues_summary.total);\n $scope.total_overdue += Number(m.issues_summary.overdue);\n $scope.total_fines += Number(m.fines_summary.balance);\n });\n $scope.loading = false;\n }, function(er) {\n alertService.addApiError(er, 'Error fetching group members for id \"' + patronid + '\"');\n $scope.loading = false;\n });\n }\n\n $scope.addExistingPatron = function(){\n var rel = 'child';\n var bvStaffDlg = $injector.get('bvStaffDlg');\n var getPatron = bvStaffDlg.patronSelect();\n return $q.when(getPatron, function(mid){\n kwApi.PatronGroup.addGroupMember({id: patronid, mid: mid, rel: rel, force: 0}).$promise.then(function(){\n alertService.add({msg: \"Added group member\", type: 'success'});\n renderMembers();\n }, function(er){\n if(er.status=='409'){\n kohaDlg.dialog({ message: \"Patron is a member of another group. Add this member to this group anyway?\" }).result.then(function(rv) {\n if (rv) {\n kwApi.PatronGroup.addGroupMember({id: patronid, mid: mid, rel: rel, force: 1}).$promise.then(function(){\n alertService.add({msg: \"Added group member\", type: 'success'});\n renderMembers();\n }, function(fail){\n alertService.addApiError(fail,'Adding group member failed');\n });\n }\n });\n } else {\n alertService.addApiError(er,'Adding group member failed');\n }\n });\n }, function(fail){\n console.warn(fail);\n });\n\n };\n\n $scope.removePatron = function(i){\n kohaDlg.dialog({ message: \"Confirm patron removal from this group.\" }).result.then(function(rv) {\n if (rv) {\n kwApi.PatronGroup.rmGroupMember({id: patronid, mid: $scope.members[i].borrowernumber}, function(){\n $scope.members.splice(i,1);\n }, function(fail){\n alertService.addApiError(fail,'Removal of group member failed');\n });\n }\n });\n }\n\n $scope.removeDependentPatron = function(){\n kohaDlg.dialog({ message: \"Confirm patron removal from this group.\" }).result.then(function(rv) {\n if (rv) {\n kwApi.PatronGroup.rmGroupMember({id: $scope.patron.guarantorid, mid: patronid}, function(){\n $scope.dependentRemoved = true;\n }, function(fail){\n alertService.addApiError(fail,'Removal of group member failed');\n });\n }\n });\n }\n\n $scope.addDependentPatron = function(){\n var rel = 'child';\n var bvStaffDlg = $injector.get('bvStaffDlg');\n var getPatron = bvStaffDlg.patronSelect();\n return $q.when(getPatron, function(pid){\n kwApi.PatronGroup.addGroupMember({id: pid, mid: patronid, rel: rel }).$promise.then(function(){\n alertService.add({msg: \"Added group member\", type: 'success'});\n }, function(er){\n alertService.addApiError(er,'Adding group member failed');\n });\n $scope.dependentRemoved = false;\n }, function(fail){\n console.warn(fail);\n });\n\n };\n\n $scope.userCan = {\n createBorrowers: userService.can({borrowers: {'create': '*'}}),\n };\n\n var once = false;\n $scope.isDependent = function() {\n if ($scope.patron.guarantorid){\n if (!once) {\n $http.get('/api/patron/'+ $scope.patron.guarantorid +'/name').then(function (response) {\n var firstname = response.data.firstname;\n var surname = response.data.surname;\n $scope.guarantorName = firstname + \" \" + surname;\n });\n }\n once = true;\n return true;\n }\n else {return false};\n }\n\n if (!$scope.isDependent()) renderMembers();\n}])\n\n.controller('StaffPatronMergeCtrl', [\"$scope\", \"$state\", \"kwApi\", \"alertService\", \"kohaDlg\", \"$q\", \"$location\", \"bvStaffDlg\", \"$http\", function ($scope, $state, kwApi, alertService, kohaDlg, $q, $location, bvStaffDlg, $http) {\n\n var patronid = $state.params.borrowernumber;\n $scope.showMergeBtn = false;\n $scope.mergePID = undefined;\n\n $scope.mergePatron = function(){\n kohaDlg.dialog({ message: \"WARNING: this action cannot be reversed. Confirm merge with patron \" +\n $scope.mergeName + \" (\" + $scope.mergePID + \").\" }).result.then(function(rv) {\n if (rv) {\n var flat_bfields = [];\n Object.keys($scope.bfields).forEach(function(k){\n if($scope.bfields[k].value === false){\n flat_bfields.push(k);\n }\n });\n kwApi.Patron.mergePatron({source_patron_id: patronid, target_patron_id: $scope.mergePID, bfields: flat_bfields}).$promise.then(function(){\n alertService.add({msg: \"Merged patron with borrowernumber \" + $scope.mergePID, type: 'success'});\n $location.url('/app/staff/patron/' + $scope.mergePID + '/details');\n }, function(e){\n alertService.addApiError(e,'Merging patron failed');\n });\n }\n });\n };\n\n $scope.cancelMerge = function(){\n $scope.showMergeBtn = false;\n $scope.mergePID = undefined;\n $scope.mergeName = undefined;\n };\n\n $scope.selectPatron = function(){\n var getPatron = bvStaffDlg.patronSelect();\n return $q.when(getPatron, function(pid){\n if (pid == patronid) {\n alertService.add({msg: \"Cannot merge a record with iteself.\", type: 'danger'});\n $scope.cancelMerge();\n }\n else {\n $http.get('/api/patron/'+ pid +'/name').then(function (r) {\n var firstname = r.data.firstname;\n var surname = r.data.surname;\n $scope.mergeName = firstname + \" \" + surname;\n });\n $scope.mergePID = pid;\n $scope.showMergeBtn = true;\n }\n }, function(fail){\n console.warn(fail);\n });\n };\n\n $scope.bfields = {\n title: {label: 'Salutation', value: true},\n surname: {label: 'Surname', value: true},\n firstname: {label: 'First name', value: true},\n dateofbirth: {label: 'Date of birth', value: true},\n initials: {label: 'Initials', value: true},\n othernames: {label: 'Other name', value: true},\n sex: {label: 'Sex', value: true},\n guarantorid: {label: 'Guarantor ID (hidden)', value: true},\n contactname: {label: 'Guarantor Surname', value: true},\n contactfirstname: {label: 'Guarantor First name', value: true},\n relationship: {label: 'Guarantor Relationship', value: true},\n address: {label: 'Main Address', value: true},\n address2: {label: 'Main Address 2', value: true},\n city: {label: 'Main Address City', value: true},\n zipcode: {label: 'Main Address Zipcode', value: true},\n country: {label: 'Main Address Country', value: true},\n phone: {label: 'Contact Phone (primary)', value: true},\n phonepro: {label: 'Contact Phone (secondary)', value: true},\n mobile: {label: 'Contact Phone (cell)', value: true},\n smsalertnumber: {label: 'SMS alert (cell)', value: true},\n email: {label: 'Email (primary)', value: true},\n emailpro: {label: 'Email (secondary)', value: true},\n fax: {label: 'Fax', value: true},\n B_address: {label: 'Alternate Address', value: true},\n B_address2: {label: 'Alternate Address 2', value: true},\n B_city: {label: 'Alternate City', value: true},\n B_zipcode: {label: 'Alternate Zipcode', value: true},\n B_country: {label: 'Alternate Country', value: true},\n B_phone: {label: 'Alternate Phone', value: true},\n B_email: {label: 'Alternate Email', value: true},\n contactnote: {label: 'Alternate Contact Note', value: true},\n altcontactsurname: {label: 'Alternate Contact Surname', value: true},\n altcontactfirstname: {label: 'Alternate Contact First name', value: true},\n altcontactaddress1: {label: 'Alternate Contact Address', value: true},\n altcontactaddress2: {label: 'Alternate Contact Address 2', value: true},\n altcontactaddress3: {label: 'Alternate Contact City, State', value: true},\n altcontactzipcode: {label: 'Alternate Contact Zipcode', value: true},\n altcontactcountry: {label: 'Alternate Contact Country', value: true},\n altcontactphone: {label: 'Alternate Contact Phone', value: true},\n cardnumber: {label: 'Card number', value: true},\n branchcode: {label: 'Library', value: true},\n categorycode: {label: 'Category', value: true},\n sort1: {label: 'Sort 1', value: true},\n sort2: {label: 'Sort 2', value: true},\n dateenrolled: {label: 'Registration date', value: true},\n dateexpiry: {label: 'Expiry date', value: true},\n opacnote: {label: 'Discovery Layer note', value: true},\n borrowernotes: {label: 'Circulation note', value: true},\n userid: {label: 'DL Login', value: true},\n password: {label: 'Password', value: true},\n prefs: {label: 'User Preferences', value: true},\n xattr: {label: 'Additional Patron Attributes', value: true}\n };\n\n $scope.selectBfields = function(){\n Object.keys($scope.bfields).forEach(function(k){\n $scope.bfields[k].value = true;\n }\n )};\n\n $scope.clearBfields = function(){\n Object.keys($scope.bfields).forEach(function(k){\n $scope.bfields[k].value = false;\n }\n )};\n\n}])\n\n.controller('StaffPatronDeleteCtrl', [\"$scope\", \"$state\", \"kwApi\", \"alertService\", \"kohaDlg\", \"$injector\", \"$q\", \"$location\", function ($scope, $state, kwApi, alertService, kohaDlg, $injector, $q, $location) {\n\n $scope.loading = true;\n var patronid = $state.params.borrowernumber;\n\n kwApi.Patron.deletePatron({id: patronid, test: true}).$promise.then(function(){\n $scope.cannotDelete = false;\n }, function(e){\n $scope.cannotDeleteError = e.data;\n $scope.cannotDelete = true;\n });\n\n var renderMembers = function () {\n kwApi.PatronGroup.getGroup({id: patronid}).$promise.then(function(rv) {\n $scope.members = rv;\n $scope.total_holds = 0;\n $scope.total_issues = 0;\n $scope.total_overdue = 0;\n $scope.total_fines = 0;\n rv.forEach(function(m){\n $scope.total_holds += Number(m.holds_summary.total);\n $scope.total_issues += Number(m.issues_summary.total);\n $scope.total_overdue += Number(m.issues_summary.overdue);\n $scope.total_fines += Number(m.fines_summary.balance);\n });\n $scope.loading = false;\n }, function(er) {\n alertService.addApiError(er, 'Error fetching group members for id \"' + patronid + '\"');\n $scope.loading = false;\n });\n }\n\n $scope.removePatron = function(i){\n kohaDlg.dialog({ message: \"Confirm patron removal from this group.\" }).result.then(function(rv) {\n if (rv) {\n kwApi.PatronGroup.rmGroupMember({id: patronid, mid: $scope.members[i].borrowernumber}, function(){\n $scope.members.splice(i,1);\n }, function(fail){\n alertService.addApiError(fail,'Removal of group member failed');\n });\n }\n });\n }\n\n $scope.removeDependentPatron = function(){\n kohaDlg.dialog({ message: \"Confirm patron removal from this group.\" }).result.then(function(rv) {\n if (rv) {\n kwApi.PatronGroup.rmGroupMember({id: $scope.patron.guarantorid, mid: patronid}, function(){\n $scope.dependentRemoved = true;\n }, function(fail){\n alertService.addApiError(fail,'Removal of group member failed');\n });\n }\n });\n }\n\n $scope.relinkPatron = function(i){\n var rel = 'child';\n var bvStaffDlg = $injector.get('bvStaffDlg');\n var getPatron = bvStaffDlg.patronSelect();\n return $q.when(getPatron, function(pid){\n kwApi.PatronGroup.addGroupMember({id: pid, mid: $scope.members[i].borrowernumber, rel: rel, force: 1}).$promise.then(function(){\n alertService.add({msg: \"Added group member\", type: 'success'});\n $scope.members.splice(i,1);\n }, function(e){\n alertService.addApiError(e,'Adding group member failed');\n });\n }, function(fail){\n console.warn(fail);\n });\n\n };\n\n $scope.deleteDependentPatron = function(i){\n $scope.deletePatron($scope.members[i].borrowernumber, true, i);\n }\n\n $scope.deletePatron = function(id, remove, i){\n kohaDlg.dialog({ message: \"Confirm patron id \" + id + \" deletion.\" }).result.then(function(rv) {\n if (rv) {\n kwApi.Patron.deletePatron({id: id}).$promise.then(function(){\n alertService.add({msg: \"Deleted patron id \" + id, type: 'success'});\n if (remove) {\n $scope.members.splice(i,1);\n }\n else {\n $location.url('/app/staff/patrons/browse');\n }\n }, function(e){\n alertService.addApiError(e,'Deleting patron failed');\n });\n }\n });\n }\n\n $scope.isDependent = function() {\n if ($scope.patron.guarantorid){return true;}\n else {return false};\n }\n\n if (!$scope.isDependent()) renderMembers();\n}])\n.controller('StaffPatronLostItemsCtrl', [\"$scope\", \"$state\", \"$q\", \"kwApi\", \"alertService\", \"userService\", \"bibService\", \"kohaDlg\", function ($scope, $state, $q, kwApi, alertService, userService, bibService, kohaDlg) {\n\n var patronid = $state.params.borrowernumber;\n\n // this controller should be a child of StaffPatronCtrl ??\n\n // $scope.patron = kwApi.Patron.get({id: patronid});\n\n $scope.order = { field: 'date_lost', reverse: false };\n\n var compose_fee_summary = function(fees){\n var fee_summary = {\n date: (fees.length) ? fees[0].timestamp : null,\n amount: fees.reduce(function(tot,fee){ return tot + fee.amount; }, 0),\n outstanding: fees.reduce(function(tot,fee){ return tot + fee.outstanding; }, 0),\n settled: fees.reduce(\n function(sdate,fee){\n if(fee.outstanding) return null;\n else return ( sdate && fee.settlement_date < sdate) ? sdate : fee.settlement_date;\n }, null),\n creditByDate: []\n };\n fees.forEach(function(fee){\n fee.credits.forEach(function(credit){\n var creditDate = credit.timestamp.substring(0,10);\n var creditByDate = fee_summary.creditByDate.find(function(c){ return c.date == creditDate; });\n if(!creditByDate){\n creditByDate = { date: creditDate,\n accttype: {}\n };\n fee_summary.creditByDate.push( creditByDate );\n }\n\n var creditByType = creditByDate.accttype[credit.accounttype];\n if( creditByType ){\n creditByType.amount -= credit.amount;\n creditByType.inFull = creditByType.amount == fee_summary.amount;\n } else {\n creditByDate.accttype[credit.accounttype] = {\n amount: credit.amount * -1,\n inFull: credit.amount == fee_summary.amount * -1\n }\n }\n });\n });\n return fee_summary;\n };\n\n $scope.acctLabel = {};\n kwApi.AccountType.getList({},function(ats){\n ats.forEach(function(accttype){\n $scope.acctLabel[accttype.accounttype] = accttype.description;\n });\n });\n\n $scope.linked = {};\n\n function updateLinkedFees (li){\n kwApi.Fee.getFromLostItem({lostid: li.id}, function(fees){\n $scope.linked[li.id].fees = compose_fee_summary(fees);\n });\n }\n\n $scope.lostitems = kwApi.LostItem.getforPatron({id: patronid}, function(lostitems){\n lostitems.forEach(function(li){\n\n $scope.linked[li.id] = {\n _display: { title : li.title }\n };\n $scope.linked[li.id].item = li._embed.item;\n if(!li._embed.item) $scope.linked[li.id].item_deleted = true;\n $scope.linked[li.id].can_remove = userService.can({borrowers: 'delete_lost_items'}) &&\n ( li.persist || $scope.linked[li.id].item_deleted );\n updateLinkedFees(li);\n });\n $scope.loaded = true;\n\n $q.all( lostitems.map(\n function(li){ return $scope.linked[li.id].item.$promise; })\n ).finally(function(){\n $scope.lostitems.forEach(function(li){\n bibService.get( li.biblionumber ).then(function(bib){\n $scope.linked[li.id]._display.title = bib.title_ext;\n })\n })\n });\n });\n\n $scope.claimReturned = function(lostitem, undo){\n\n var msg = (undo) ? \"Undo Claimed Returned ?\" :\n \"Set Claimed Returned ?\";\n\n kohaDlg.dialog({ message: msg }).result.then(function(rv) {\n if (rv) {\n lostitem.$claimReturned({ undo: undo }, function(li){\n updateLinkedFees(li);\n });\n }\n });\n };\n $scope.rmLostItem = function(lostitem){\n kohaDlg.dialog({ message: \"Delete Lost Item record? This action cannot be undone.\" }).result.then(function(rv) {\n if (rv) {\n lostitem.$delete({}, function(ok){\n var i = $scope.lostitems.indexOf(lostitem);\n $scope.lostitems.splice(i,1);\n }, function(fail){\n alertService.addApiError(fail,'Delete failed');\n });\n }\n });\n }\n\n}])\n\n\n// Serials\n.controller('StaffSerialsCtrl', [\"$scope\", \"$state\", \"userService\", function ($scope, $state, userService) {\n var userBranch = userService.login_branch;\n $scope.user = userService;\n $scope.userCanAccessBranch = function(branchCode) {\n return $scope.user.canInBranch({serials: 'base'}, branchCode);\n };\n\n if ($state.current.name == 'staff.serials') {\n $state.go('staff.serials.index');\n }\n}])\n\n.controller('StaffSerialsIndexCtrl', [\"$scope\", \"periodicals\", \"kwApi\", \"alertService\", \"$state\", \"kohaDlg\", \"$stateParams\", \"loading\", \"configService\", function ($scope, periodicals, kwApi, alertService, $state, kohaDlg, $stateParams, loading, configService) {\n\n $scope.periodicals = periodicals;\n $scope.periodicals.forEach(function(p) {\n p._available_subscribed_branches = p.subscribed_branches.filter(function(b) {\n return $scope.user.canInBranch({serials: 'base'}, b.branch_code);\n });\n p._available_total_subscriptions = p._available_subscribed_branches.length;\n });\n\n $scope.order = {field: 'work.title', reverse: false};\n\n $scope.currentPage = 1;\n $scope.totalRows = (periodicals[0] || {})._embed_total_rows;\n\n $scope.refresh = function() {\n var start = ($scope.currentPage -1) * 20;\n loading.wrap(\n kwApi.Periodical.search({title: $stateParams.title, issn: $stateParams.issn, start: start, count: 20, sort: $scope.order.field, reverse: ($scope.order.reverse ? 1 : 0)}).$promise,\n \"Unable to load\"\n ).then(function(rv) {\n $scope.periodicals = rv;\n $scope.periodicals.forEach(function(p) {\n p._available_subscribed_branches = p.subscribed_branches.filter(function(b) {\n return $scope.user.canInBranch({serials: 'base'}, b.branch_code);\n });\n p._available_total_subscriptions = p._available_subscribed_branches.length;\n });\n });\n };\n\n $scope.pageChanged = function() {\n $scope.refresh();\n };\n\n $scope.sortChanged = function() {\n $scope.currentPage = 1;\n $scope.refresh();\n };\n\n $scope.publicationDelete = function(id) {\n var rv;\n if (configService.SerialDeleteItem === 'ask') {\n rv = kohaDlg.dialog({\n heading: 'Confirm; also delete items?',\n message: 'This will remove all associated subscriptions and issues and CANNOT be undone. Do you also wish to remove all items?',\n buttons: [{val: 1, label: 'Remove Items', btnClass: 'btn-warning'}, {val: 0, label: 'Keep Items', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n });\n }\n else {\n rv = kohaDlg.dialog({\n heading: 'Are you sure?',\n message: 'Are you sure you want to delete this publication? This will remove all associated subscriptions and issues and CANNOT be undone.',\n buttons: [{val: true, label: 'Yes', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n });\n }\n rv.result.then(function(rv) {\n if (rv !== false) {\n kwApi.Periodical.deleteWithItem({id: id, item: rv},{}).$promise.then(function(rv) {\n alertService.add({msg: \"Publication deleted\"});\n $state.go('staff.serials.index', {}, {reload: true});\n }, function(err) {\n alertService.addApiError(err, 'Delete failed');\n });\n }\n });\n };\n}])\n\n.controller('StaffSerialsPeriodicalCtrl', [\"$scope\", \"periodical\", \"subscriptions\", \"serialEditions\", \"kwApi\", \"configService\", \"loading\", \"alertService\", \"bibService\", \"$uibModal\", \"$q\", \"$state\", \"kohaDlg\", \"$filter\", \"$stateParams\", function($scope, periodical, subscriptions, serialEditions, kwApi, configService,\n loading, alertService, bibService, $uibModal, $q, $state, kohaDlg, $filter, $stateParams) {\n $scope.periodical = periodical;\n\n\n\n if (!periodical.id) {\n periodical.bib_id = $stateParams.bib_id;\n periodical.work = {};\n periodical.receipt_patterns = [];\n periodical.next_edition = {};\n periodical.auto_create_editions = true;\n periodical.auto_sequence_config = true;\n periodical.sequence_variables = ['x', 'y'];\n periodical.sequence_config = {\n x: {\n add: '1',\n inner_mod: '',\n mod: '',\n linked: 'y',\n },\n y: {\n add: '1',\n inner_mod: '',\n mod: '',\n linked: '',\n },\n };\n periodical.sequence_state = {\n x: {\n count: '1',\n inner_count: '1',\n },\n y: {\n count: '1',\n inner_count: '',\n },\n };\n periodical.chronology_state = dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss');\n $scope.editing = {main: true}\n $scope.expand = {\n chronology: true,\n schedule: true,\n sequence: false,\n predictions: true,\n };\n }\n else {\n $scope.editing = {main: false};\n $scope.expand = {};\n if (!periodical.next_edition) periodical.next_edition = {};\n }\n\n $scope.subscriptions = subscriptions;\n $scope.serialEditions = serialEditions;\n\n $scope.subsOrder = {field:'branch_name', reverse: false};\n $scope.editionsOrder = {field: 'publication_date', reverse: true};\n\n angular.forEach($scope.subscriptions, function(s) {\n s.branch_name = $filter('displayName')(s.branch_code, 'branch');\n });\n\n\n $scope.periodical.chronology_state_date = dayjs($scope.periodical.chronology_state).toDate();\n $scope.dpickOpened = false;\n\n $scope.dirty = false;\n \n $scope.sisterPeriodicals = [];\n $scope.refreshSisterPeriodicals = function() {\n $scope.sisterPeriodicals = [];\n if ($scope.periodical.bib_id) {\n kwApi.Periodical.getForBib({bibid: $scope.periodical.bib_id}).$promise.then(function(rv) {\n rv.forEach(function(p) {\n if (p.id != $scope.periodical.id) {\n $scope.sisterPeriodicals.push(p);\n }\n });\n });\n }\n };\n $scope.$watch('periodical.bib_id', function(newVal, oldVal) {\n if (newVal || oldVal) {\n $scope.refreshSisterPeriodicals();\n }\n });\n\n $scope.findCatalogRecord = function() {\n if (!$scope.periodical.work && ($scope.periodical.work.title || $scope.periodical.bib_id)) return;\n var query = $scope.periodical.work.title || ('biblionumber:' + $scope.periodical.bib_id);\n var parentScope = $scope;\n loading.add();\n $uibModal.open({\n title: 'Find Catalog Record',\n templateUrl: '/app/static/partials/staff/serials/catalog-select.html',\n size: 'lg',\n backgroundClick: false,\n resolve: {\n records: function() {\n return kwApi.Catalog.query({query: query, count: 10, fq: 'lost:*'}).$promise.then(function(rv) {\n rv.hits.forEach(function(hit) {\n hit.id = hit.work.substr(10);\n });\n return rv;\n });\n },\n title: function() {\n return $scope.periodical.work.title;\n }\n },\n controller: [\"$scope\", \"records\", \"title\", function($scope, records, title) {\n loading.resolve();\n $scope.currentPage = 1;\n $scope.pageChanged = function() {\n var start = ($scope.currentPage - 1)*10;\n loading.add();\n kwApi.Catalog.query({query: title, start: start, count: 10}).$promise.then(function(rv) {\n rv.hits.forEach(function(hit) {\n hit.id = hit.work.substr(10);\n });\n $scope.records = rv;\n loading.resolve();\n });\n };\n\n $scope.records = records;\n $scope.select = function(bib) {\n parentScope.periodical.bib_id = bib.id;\n parentScope.periodical.work.title = bib.title;\n $scope.$close();\n };\n }]\n });\n };\n\n $scope.publicationDelete = function() {\n var rv;\n if (configService.SerialDeleteItem === 'ask') {\n rv = kohaDlg.dialog({\n heading: 'Confirm; also delete items?',\n message: 'This will remove all associated subscriptions and issues and CANNOT be undone. Do you also wish to remove all items?',\n buttons: [{val: 1, label: 'Remove Items', btnClass: 'btn-warning'}, {val: 0, label: 'Keep Items', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n });\n }\n else {\n rv = kohaDlg.dialog({\n heading: 'Are you sure?',\n message: 'Are you sure you want to delete this publication? This will remove all associated subscriptions and issues and CANNOT be undone.',\n buttons: [{val: true, label: 'Yes', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n });\n }\n rv.result.then(function(rv) {\n if (rv !== false) {\n kwApi.Periodical.deleteWithItem({id: $scope.periodical.id, item: rv},{}).$promise.then(function(rv) {\n alertService.add({msg: \"Publication deleted\"});\n $state.go('staff.serials.index');\n }, function(err) {\n alertService.addApiError(err, 'Delete failed');\n });\n }\n });\n };\n\n\n $scope.publicationEdit = function() {\n $scope.orig = angular.copy($scope.periodical);\n $scope.dirty = false;\n $scope.editing.main = true;\n $scope.expand._prev_chronology = $scope.expand.chronology;\n $scope.expand._prev_schedule = $scope.expand.schedule;\n //$scope.expand._prev_sequence = $scope.expand.sequence;\n $scope.expand.chronology = true;\n $scope.expand.schedule = true;\n //$scope.expand.sequence = true;\n };\n\n $scope.publicationEditCancel = function() {\n $scope.dirty = false;\n $scope.periodical = $scope.orig;\n angular.extend($scope.periodical,$scope.orig);\n $scope.editing.main = false;\n $scope.expand.chronology = $scope.expand._prev_chronology;\n $scope.expand.schedule = $scope.expand._prev_schedule;\n //$scope.expand.sequence = $scope.expand._prev_sequence;\n };\n\n $scope.publicationEditSave = function() {\n var isNew = false;\n loading.add();\n $scope.periodical.chronology_state =\n dayjs($scope.periodical.chronology_state_date).startOf('day').format('YYYY-MM-DD HH:mm:ss');\n var promise;\n if ($scope.periodical.id) {\n promise = kwApi.Periodical.put({id: $scope.periodical.id}, $scope.periodical).$promise;\n }\n else {\n promise = kwApi.Periodical.save($scope.periodical).$promise;\n isNew = true;\n }\n\n promise.then(function(rv) {\n if (isNew) {\n return $q.when(rv);\n }\n else {\n return kwApi.Periodical.get({id: $scope.periodical.id}).$promise;\n }\n }).then(function(obj) {\n if (isNew) {\n $state.go('staff.serials.periodical', {id: obj.id});\n }\n angular.extend($scope.periodical,obj);\n $scope.periodical.chronology_state_date = dayjs($scope.periodical.chronology_state).toDate();\n $scope.editing.main = false;\n $scope.expand.chronology = $scope.expand._prev_chronology;\n $scope.expand.schedule = $scope.expand._prev_schedule;\n $scope.expand.sequence = $scope.expand._prev_sequence;\n $scope.dirty = false;\n loading.resolve();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err, 'Save failed');\n });\n };\n\n $scope.resetSequenceConfig = function() {\n $scope.periodical.auto_sequence_config = true;\n $scope.expand.predictions = true;\n $scope.refreshPredictions();\n };\n\n\n $scope.refreshPredictions = function() {\n if ($scope.editing.main || $scope.expand.predictions) {\n $scope.refreshingPredictions = true;\n var fmtDate = dayjs($scope.periodical.chronology_state_date).startOf('day').format('YYYY-MM-DD HH:mm:ss');\n kwApi.Periodical.predict({infer: ($scope.periodical.auto_sequence_config ? 1 : 0)}, {\n patterns: $scope.periodical.receipt_patterns,\n sequence_config: $scope.periodical.sequence_config,\n chronology_state: fmtDate,\n sequence_state: $scope.periodical.sequence_state,\n pattern_index: $scope.periodical.pattern_index,\n }).$promise.then(function(rv) {\n $scope.predictions = rv;\n if (rv && rv.length > 0) {\n $scope.predictionWarning = rv[0].prediction_warning;\n $scope.periodical.next_edition.chronology_vintage = rv[0].chronology;\n $scope.periodical.next_edition.sequence_vintage = rv[0].sequence;\n if ($scope.periodical.auto_sequence_config && rv[0].sequence_state) {\n $scope.periodical.sequence_variables.forEach(function(v) {\n if (!rv[0].sequence_state[v])\n rv[0].sequence_state[v] = {};\n if (!$scope.periodical.sequence_config[v])\n $scope.periodical.sequence_config[v] = {};\n ['mod','inner_mod','linked'].forEach(function(param) {\n $scope.periodical.sequence_config[v][param] = (rv[0].sequence_state[v]||{})[param];\n });\n });\n }\n }\n $scope.refreshingPredictions = false;\n $scope.predictionError = '';\n }, function(err) {\n $scope.refreshingPredictions = false;\n if (err.data) {\n if (/z is not defined/.test(err.data)) {\n $scope.predictionError = \"Add a 'z' parameter in the Enumeration Counters section\";\n }\n else if (/y is not defined/.test(err.data)) {\n $scope.predictionError = \"Add a 'y' parameter in the Enumeration Counters section\";\n }\n else {\n $scope.predictionError = err.data;\n }\n }\n else {\n $scope.predictionError = err;\n }\n });\n }\n };\n\n // Chronology\n //\n $scope.schedAddPattern = function(i) {\n $scope.dirty = true;\n $scope.periodical.receipt_patterns.splice(i,0,{\n start_date: '01/01',\n end_date: '12/31',\n pattern: undefined,\n pattern_description: 'None',\n chronology_template: '',\n sequence_template: '',\n sequence_increment: '1',\n });\n $scope.schedEditPattern(i);\n };\n\n $scope.schedSaveTemplate = function() {\n var obj = {\n patterns: JSON.stringify(periodical.receipt_patterns),\n name: $scope.periodical._schedSaveName,\n description: $scope.periodical._schedSaveName,\n };\n kwApi.SerialPatternTemplate.save(obj).$promise.then(function(rv) {\n alertService.add({msg: \"Scheduled saved as template \" + $scope.periodical._schedSaveName, type: \"success\"});\n }, function(err) {\n alertService.addApiError(err, 'Save schedule template failed');\n });\n };\n\n $scope.schedDelPattern = function(i) {\n $scope.dirty = true;\n $scope.periodical.receipt_patterns.splice(i,1);\n };\n\n $scope.schedEditPattern = function(i) {\n var parentScope = $scope;\n $uibModal.open({\n title: 'Chronology and Sequence Pattern',\n templateUrl: '/app/static/partials/staff/serials/pattern-edit.html',\n resolve: {\n parentReceiptPattern: function() {\n return parentScope.periodical.receipt_patterns[i];\n },\n pattern: function() {\n var c = angular.copy(parentScope.periodical.receipt_patterns[i]);\n if (c.sequence_increment === undefined || c.sequence_increment === null) {\n c.sequence_increment = '1';\n }\n return c;\n },\n serial_pattern_templates: function() {\n return kwApi.SerialPatternTemplate.query().$promise;\n },\n serial_chronology_templates: function() {\n return kwApi.SerialChronologyTemplate.query().$promise;\n },\n serial_sequence_templates: function() {\n return kwApi.SerialSequenceTemplate.query().$promise;\n },\n },\n size: 'lg',\n controller: [\"$scope\", \"pattern\", \"parentReceiptPattern\", \"serial_pattern_templates\", \"serial_chronology_templates\", \"serial_sequence_templates\", \"$uibModal\", function($scope, pattern, parentReceiptPattern, serial_pattern_templates, serial_chronology_templates, serial_sequence_templates, $uibModal) {\n $scope.pattern = pattern;\n\n $scope.isMultiMonth = function(p) {\n if (!p) return;\n var halves = (p || \"\").split('*');\n if (halves.length != 2) return;\n var lhs = halves[0].split(':');\n if (lhs.length != 2) return;\n return (lhs[1] > 1);\n };\n\n $scope.showHelp = function(s) {\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/serials/help-' + s + '.html',\n size: 'md'\n });\n };\n\n $scope.serialPatternTemplates = serial_pattern_templates;\n\n $scope.serialPatternTemplates.forEach(function(v) {\n var p = v.patterns;\n if ((p.length > 1) || (p[0].start_date != '01-01') || (p[0].end_date != '12-31')) {\n // FIXME this is gross\n v.template = '#' + v.id;\n v._is_advanced = true;\n }\n else {\n v.template = v.patterns[0].pattern;\n }\n });\n $scope.selectedPatternTemplate = $scope.serialPatternTemplates.find(function(v) {\n return v._is_basic && v.template == $scope.pattern.pattern;\n });\n if (!$scope.selectedPatternTemplate) {\n $scope.serialPatternTemplates.push({\n template: $scope.pattern.pattern\n });\n }\n\n $scope.serialChronologyTemplates = serial_chronology_templates;\n if ($scope.pattern.chronology_template && $scope.serialChronologyTemplates.find(function(v) {\n return v.template == $scope.pattern.chronology_template;\n }) === undefined) {\n $scope.serialChronologyTemplates.push({\n template: $scope.pattern.chronology_template\n });\n }\n\n $scope.serialSequenceTemplates = serial_sequence_templates;\n if ($scope.pattern.sequence_template && $scope.serialSequenceTemplates.find(function(v) {\n return v.template == $scope.pattern.sequence_template;\n }) === undefined) {\n $scope.serialSequenceTemplates.push({\n template: $scope.pattern.sequence_template\n });\n }\n\n var bvDateRecur = $filter('bvDateRecur');\n\n $scope.patternSelectConfig = {\n maxItems: 1,\n valueField: 'template',\n labelField: 'name',\n searchField: ['name','description'],\n create: true,\n render: {\n item: function(item, escape) {\n if (!item) return '';\n var t = '
    ';\n if (item.name)\n t = t + escape(item.name);\n else \n t = t + '(unnamed)';\n t = t + '
    ';\n return t;\n },\n option: function(item, escape) {\n if (!item) return '';\n var t = '
    ';\n if (item.name)\n t = t + escape(item.name);\n else \n t = t + '(unnamed)';\n t = t + ' - ';\n if (item._is_advanced)\n t = t + '[Advanced Pattern]';\n else\n t = t + escape(bvDateRecur(item.template));\n t = t + '';\n if (item.description)\n t = t + '
    ' + escape(item.description) + '
    ';\n t = t + '
    ';\n return t;\n }\n },\n };\n\n\n $scope.chronSelectConfig = $scope.enumSelectConfig = {\n maxItems: 1,\n valueField: 'template',\n labelField: 'name',\n searchField: ['name','description'],\n create: true,\n render: {\n item: function(item, escape) {\n if (!item) return '';\n var t = '
    ';\n if (item.name)\n t = t + escape(item.name);\n else \n t = t + '(unnamed)';\n t = t + ' - ' + escape(item.template) + '
    ';\n return t;\n },\n option: function(item, escape) {\n if (!item) return '';\n var t = '
    ';\n if (item.name)\n t = t + escape(item.name);\n else \n t = t + '(unnamed)';\n t = t + ' - ' + escape(item.template) + '
    ';\n return t;\n },\n },\n };\n\n $scope.pattern.start_month = $scope.pattern.start_date.substr(0,2);\n $scope.pattern.start_day = $scope.pattern.start_date.substr(3,2);\n\n $scope.pattern.end_month = $scope.pattern.end_date.substr(0,2);\n $scope.pattern.end_day = $scope.pattern.end_date.substr(3,2);\n\n $scope.$watch('pattern.pattern', function(newVal) {\n if (newVal) {\n $scope.selectedPatternTemplate = undefined;\n if (newVal.substr(0,1) == '#') {\n var id = newVal.substr(1);\n $scope.selectedPatternTemplate = $scope.serialPatternTemplates.find(function(v) {\n return (v.id == id);\n });\n }\n else {\n $scope.selectedPatternTemplate = $scope.serialPatternTemplates.find(function(v) {\n return v.template == newVal;\n });\n }\n if ($scope.selectedPatternTemplate) {\n pattern.pattern_description = $scope.selectedPatternTemplate.name;\n if (! $scope.selectedPatternTemplate._is_advanced) {\n var p = $scope.selectedPatternTemplate.patterns[0];\n if (!$scope.pattern.start_month && p.start_month)\n $scope.pattern.start_month = p.start_month;\n if (!$scope.pattern.end_month && p.end_month)\n $scope.pattern.end_month = p.end_month;\n if (!$scope.pattern.chronology_template && p.chronology_template)\n $scope.pattern.chronology_template = p.chronology_template;\n if (!$scope.pattern.sequence_template && p.sequence_template)\n $scope.pattern.sequence_template = p.sequence_template;\n }\n }\n }\n\n });\n\n\n $scope.save = function() {\n if ($scope.pattern.chronology_template_save_as) {\n kwApi.SerialChronologyTemplate.save({\n name: $scope.pattern.chronology_template_save_as,\n description: $scope.pattern.chronology_template_save_as,\n template: $scope.pattern.chronology_template,\n }).$promise.then(function(rv) {\n alertService.add({msg: \"Chronology saved as new template\", type: \"success\"});\n }, function(err) {\n alertService.addApiError(err, \"Can't save chronology template\");\n });\n }\n if ($scope.pattern.sequence_template_save_as) {\n kwApi.SerialSequenceTemplate.save({\n name: $scope.pattern.sequence_template_save_as,\n description: $scope.pattern.sequence_template_save_as,\n template: $scope.pattern.sequence_template,\n }).$promise.then(function(rv) {\n alertService.add({msg: \"Enumeration saved as new template\", type: \"success\"});\n }, function(err) {\n alertService.addApiError(err, \"Can't save enumeration template\");\n });\n }\n\n var p = $scope.selectedPatternTemplate;\n if (p && p._is_advanced) {\n parentScope.periodical.receipt_patterns = [];\n p.patterns.forEach(function(pattern) {\n parentScope.periodical.receipt_patterns.push(pattern);\n });\n }\n else {\n $scope.pattern.start_date = $scope.pattern.start_month + '-' + $scope.pattern.start_day;\n $scope.pattern.end_date = $scope.pattern.end_month + '-' + $scope.pattern.end_day;\n angular.extend(parentReceiptPattern, $scope.pattern);\n }\n\n parentScope.dirty = true;\n $scope.$close();\n };\n }]\n });\n };\n\n\n $scope.$watch('periodical.receipt_patterns', function(newVal, oldVal) {\n if (newVal) {\n //$scope.expand.predictions = false;\n $scope.refreshPredictions();\n if (oldVal && !angular.equals(newVal,oldVal)) {\n $scope.dirty = true;\n }\n }\n }, true);\n\n // Sequence\n\n var knownSequenceVariables = ['x','y','z'];\n var knownSequenceParameters = ['x','y','z'];\n\n $scope.$watch('periodical.sequence_config', function(newVal, oldVal) {\n $scope.periodical.sequence_variables.forEach(function(k) {\n var linked = newVal[k].linked;\n if (typeof(linked) == 'string' && linked != '' && newVal[linked]) {\n newVal[k].inner_mod = newVal[linked].mod;\n }\n });\n if (newVal) {\n $scope.expand.predictions = false;\n //$scope.refreshPredictions();\n if (oldVal && !angular.equals(newVal,oldVal)) {\n $scope.dirty = true;\n }\n }\n }, true);\n\n $scope.$watch('periodical.sequence_state', function(newVal, oldVal) {\n $scope.periodical.sequence_variables.forEach(function(k) {\n var linked = $scope.periodical.sequence_config[k].linked;\n if (typeof(linked) == 'string' && linked != '' && newVal[linked]) {\n newVal[k].inner_count = newVal[linked].count;\n }\n });\n if (newVal) {\n $scope.expand.predictions = false;\n //$scope.refreshPredictions();\n if (oldVal && !angular.equals(newVal,oldVal)) {\n $scope.dirty = true;\n }\n }\n }, true);\n\n $scope.$watch('periodical.chronology_state_date', function(newVal, oldVal) {\n if (newVal) {\n $scope.expand.predictions = false;\n //$scope.refreshPredictions();\n if (oldVal && newVal !== oldVal) {\n $scope.dirty = true;\n }\n }\n });\n\n $scope.$watch('expand.predictions', function(newVal) {\n if (newVal) {\n $scope.refreshPredictions();\n }\n });\n\n $scope.seqAddVar = function() {\n var nextVar = knownSequenceVariables[$scope.periodical.sequence_variables.length];\n $scope.periodical.sequence_variables.push(nextVar);\n\n $scope.periodical.sequence_config[nextVar] = {\n add: '1',\n inner_mod: '',\n mod: '',\n linked: '',\n };\n $scope.periodical.sequence_state[nextVar] = {\n count: '1',\n inner_count: '1',\n };\n };\n\n $scope.seqDelVar = function() {\n var lastVar = $scope.periodical.sequence_variables[$scope.periodical.sequence_variables.length - 1];\n delete $scope.periodical.sequence_config[lastVar];\n delete $scope.periodical.sequence_state[lastVar];\n $scope.periodical.sequence_variables.pop();\n };\n\n $scope.subscriptionAdd = function(n) {\n $state.go('staff.serials.subscription', {id: '', parent: $scope.periodical.id});\n };\n\n $scope.subscriptionDel = function(n) {\n var rv;\n if (configService.SerialDeleteItem === 'ask') {\n rv = kohaDlg.dialog({\n heading: 'Confirm; also delete items?',\n message: 'This will remove all associated issues for this branch and CANNOT be undone. Do you also wish to remove all items?',\n buttons: [{val: 1, label: 'Remove Items', btnClass: 'btn-warning'}, {val: 0, label: 'Keep Items', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n });\n }\n else {\n rv = kohaDlg.dialog({\n heading: 'Are you sure?',\n message: 'Are you sure you want to delete this subscription? This will remove all associated issues for this branch and CANNOT be undone.',\n buttons: [{val: true, label: 'Yes', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n });\n }\n rv.result.then(function(rv) {\n console.log(rv);\n if (rv !== false) {\n kwApi.Subscription.deleteWithItem({id: n, item: rv},{}).$promise.then(function(rv) {\n alertService.add({msg: \"Subscription deleted\"});\n kwApi.Periodical.getSubscriptions({id: $scope.periodical.id}).$promise.then(function(subs) {\n $scope.subscriptions = subs;\n angular.forEach($scope.subscriptions, function(s) {\n s.branch_name = $filter('displayName')(s.branch_code, 'branch');\n });\n });\n }, function(err) {\n alertService.addApiError(err, 'Delete failed');\n });\n }\n });\n };\n\n $scope.editionEdit = function(n) {\n $state.go('staff.serials.edition', {id: n});\n };\n\n $scope.editionAdd = function() {\n kwApi.Periodical.generateEdition({id: $scope.periodical.id},{}).$promise.then(function(e) {\n return kwApi.Periodical.getSerialEditions({id: $scope.periodical.id}).$promise;\n }).then(function(rv) {\n $scope.serialEditions = rv;\n return kwApi.Periodical.get({id: $scope.periodical.id}).$promise;\n }).then(function(obj) {\n angular.extend($scope.periodical,obj);\n $scope.periodical.chronology_state_date = dayjs($scope.periodical.chronology_state).toDate();\n }, function(err) {\n alertService.addApiError(err);\n });\n };\n\n $scope.editionAddSpecial = function() {\n $uibModal.open({\n title: 'New Supplemental Issue',\n templateUrl: '/app/static/partials/staff/serials/issue-supplemental-add.html',\n controller: 'StaffSerialsAddSpecialCtrl',\n resolve: {\n periodical: function() {\n return $scope.periodical;\n }\n },\n }).result.then(function() {\n kwApi.Periodical.getSerialEditions({id: $scope.periodical.id}).$promise.then(function(rv) {\n\n $scope.serialEditions = rv;\n return kwApi.Periodical.get({id: $scope.periodical.id}).$promise;\n }).then(function(obj) {\n angular.extend($scope.periodical,obj);\n $scope.periodical.chronology_state_date = dayjs($scope.periodical.chronology_state).toDate();\n }, function(err) {\n alertService.addApiError(err);\n });\n });\n };\n\n $scope.editionDel = function(n) {\n var rv;\n if (configService.SerialDeleteItem === 'ask') {\n rv = kohaDlg.dialog({\n heading: 'Confirm; also delete items?',\n message: 'This will remove all associated issues for all branches and CANNOT be undone. Do you also wish to remove all items?',\n buttons: [{val: 1, label: 'Remove Items', btnClass: 'btn-warning'}, {val: 0, label: 'Keep Items', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n });\n }\n else {\n rv = kohaDlg.dialog({\n heading: 'Are you sure?',\n message: 'Are you sure you want to delete this issue? This will remove all associated copies for all branches and CANNOT be undone.',\n buttons: [{val: true, label: 'Yes', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n });\n }\n rv.result.then(function(rv) {\n if (rv !== false) {\n kwApi.SerialEdition.deleteWithItem({id: n, item: rv},{}).$promise.then(function(rv) {\n alertService.add({msg: \"Issue deleted\"});\n kwApi.Periodical.getSerialEditions({id: $scope.periodical.id}).$promise.then(function(rv) {\n $scope.serialEditions = rv;\n });\n }, function(err) {\n alertService.addApiError(err, 'Delete failed');\n });\n }\n });\n };\n $scope.$watch('periodical.bib_id', function(bibid) {\n if (bibid) bibService.get(bibid).then(function(bib){ $scope.bib = bib; });\n });\n\n}])\n\n.controller('StaffSerialsAddSpecialCtrl', [\"$scope\", \"periodical\", \"kwApi\", \"alertService\", function($scope, periodical, kwApi, alertService) {\n $scope.periodical = periodical;\n console.dir(periodical);\n $scope.edition = {periodical_id: periodical.id, supplement_type: 'supplement'};\n $scope.save = function() {\n $scope.edition.publication_date = $scope.edition._obj_publication_date ?\n dayjs($scope.edition._obj_publication_date).startOf('day').format('YYYY-MM-DD HH:mm:ss') : null;\n if ($scope.edition.supplement_type == \"\")\n $scope.edition.supplement_type = null;\n\n kwApi.SerialEdition.save($scope.edition).$promise.then(function(rv) {\n $scope.$close();\n }, function(err) {\n alertService.addApiError(err, 'Save failed');\n });\n };\n $scope.expand = { predictions: false };\n\n $scope.refreshPredictions = function() {\n $scope.refreshingPredictions = true;\n var fmtDate = dayjs($scope.periodical.chronology_state_date).startOf('day').format('YYYY-MM-DD HH:mm:ss');\n $scope.edition.publication_date = $scope.edition._obj_publication_date ?\n dayjs($scope.edition._obj_publication_date).startOf('day').format('YYYY-MM-DD HH:mm:ss') : null;\n kwApi.Periodical.predict({infer: 0}, {\n patterns: $scope.periodical.receipt_patterns,\n sequence_config: $scope.periodical.sequence_config,\n chronology_state: fmtDate,\n sequence_state: $scope.periodical.sequence_state,\n supplement: $scope.edition,\n pattern_index: $scope.periodical.pattern_index,\n }).$promise.then(function(rv) {\n $scope.predictions = rv;\n $scope.predictions.length = 4;\n if (rv && rv.length > 0) {\n $scope.predictionWarning = rv[0].prediction_warning;\n }\n $scope.refreshingPredictions = false;\n $scope.predictionError = '';\n }, function(err) {\n $scope.refreshingPredictions = false;\n if (err.data) {\n if (/z is not defined/.test(err.data)) {\n $scope.predictionError = \"Add a 'z' parameter in the Enumeration Counters section\";\n }\n else if (/y is not defined/.test(err.data)) {\n $scope.predictionError = \"Add a 'y' parameter in the Enumeration Counters section\";\n }\n else {\n $scope.predictionError = err.data;\n }\n }\n else {\n $scope.predictionError = err;\n }\n });\n };\n\n $scope.$watch('edition', function(newVal, oldVal) {\n if (!newVal) return;\n if (angular.equals(newVal, oldVal)) return;\n if (!$scope.expand.predictions) return;\n $scope.refreshPredictions();\n }, true);\n\n $scope.$watch('expand', function(newVal, oldVal) {\n if (!$scope.expand.predictions) return;\n $scope.refreshPredictions();\n }, true);\n\n}])\n\n\n.controller('StaffSerialsSubscriptionCtrl', [\"$scope\", \"subscription\", \"serialInstances\", \"branches\", \"kwApi\", \"loading\", \"alertService\", \"$q\", \"$state\", \"configService\", \"$stateParams\", \"$filter\", function($scope, subscription, serialInstances, branches, kwApi, loading, alertService, $q, $state, configService, $stateParams, $filter) {\n $scope.subscription = subscription;\n $scope.branches = branches.filter(function(b) {\n return $scope.userCanAccessBranch(b.branchcode);\n }).sort(function(a,b) {\n return (a.branchname < b.branchname ? -1 : a.branchname > b.branchname ? 1 : 0)\n });\n $scope.editing = {};\n $scope.expand = {};\n\n $scope.vendor_accounts = [];\n $scope.purchase_orders = [];\n $scope.purchase_order_lines = [];\n\n $scope.sortableOptions = {};\n\n $scope.patronSelectConfig = {\n load: function(query, callback) {\n kwApi.Patron.query({view: 'picker', searchValue: query}).$promise.then(function(rv) {\n callback(rv);\n }, function(err) {\n callback();\n });\n },\n maxItems: 1,\n loadThrottle: 600,\n valueField: 'borrowernumber',\n labelField: 'firstname',\n searchField: ['firstname','surname'],\n render: {\n item: function(item, escape) {\n if (!item) return '';\n var branchName = $filter('displayName')(item.branch_code, 'branch');\n return '
    ' + item.firstname + ' ' + item.surname + ' (' + branchName + ')
    ';\n },\n option: function(item, escape) {\n if (!item) return '';\n var branchName = $filter('displayName')(item.branch_code, 'branch');\n return '
    ' + item.firstname + ' ' + item.surname + ' (' + branchName + ')
    ';\n }\n }\n };\n \n $scope.addToRlist = function() {\n kwApi.Patron.get({id: $scope.subscription._rlist_patron_id}).$promise.then(function(rec) {\n if (!$scope.subscription.routing_list)\n $scope.subscription.routing_list = [];\n $scope.subscription.routing_list.push({\n id: rec.borrowernumber,\n firstname: rec.firstname,\n surname: rec.surname,\n branch_code: rec.branchcode,\n branch_name: $filter('displayName')(rec.branchcode, 'branch')\n });\n $scope.subscription._rlist_patron_id = null;\n });\n };\n\n if (!subscription.id) {\n $scope.editing = {main: true}\n $scope.expand = {item: true};\n $scope._has_start_date = false;\n $scope._has_expiration_date = false;\n subscription.adds_items = false;\n subscription.adds_po_lines = false; \n subscription.item_defaults = {};\n subscription.periodical_id = $stateParams.parent;\n }\n else {\n $scope.editing = {main: false};\n if ($scope.subscription.start_date) {\n $scope._has_start_date = true;\n $scope.subscription._obj_start_date = dayjs($scope.subscription.start_date).toDate();\n }\n else {\n $scope._has_start_date = false;\n }\n if ($scope.subscription.expiration_date) {\n $scope._has_expiration_date = true;\n $scope.subscription._obj_expiration_date = dayjs($scope.subscription.expiration_date).toDate();\n }\n else {\n $scope._has_expiration_date = false;\n }\n }\n $scope.serialInstances = serialInstances;\n\n $scope.pairedOrderLines = [];\n $scope.orderLinePairing = {\n mode: 'empty',\n };\n\n $scope.refreshOrderLines = function() {\n kwApi.Branch.getAcqSubscriptionPOLines({id: $scope.subscription.branch_code, subscription_id: $scope.subscription.id}).$promise.then(function(rv) {\n $scope.pairedOrderLines = rv;\n \n if ($scope.pairedOrderLines.length == 0) {\n if ($scope.subscription.purchase_order_number) {\n $scope.orderLinePairing.mode = 'unpaired';\n }\n else {\n $scope.orderLinePairing.mode = 'empty';\n }\n }\n else if ($scope.pairedOrderLines.length > 1) {\n $scope.orderLinePairing.mode = 'multiple';\n }\n else if ($scope.subscription.purchase_order_number == rv[0].purchase_order_number) {\n $scope.orderLinePairing.mode = 'paired';\n }\n else {\n $scope.orderLinePairing.mode = 'override';\n $scope.orderLinePairing.overrideOrder = rv[0].purchase_order_number;\n $scope.orderLinePairing.overrideOrderLineID = rv[0].id;\n $scope.orderLinePairing.overrideOrderLineTitle = rv[0].title;\n }\n });\n };\n\n\n $scope.$watch('subscription.branch_code', function(newVal) {\n if (newVal) {\n kwApi.Branch.getAcqVendorAccounts({id: newVal}).$promise.then(function(rv) {\n $scope.vendor_accounts = rv;\n if ($scope.subscription.vendor_account_id) {\n rv.forEach(function(va) {\n if (va.id == $scope.subscription.vendor_account_id) {\n $scope.subscription.vendor_account_name = va.name;\n }\n });\n }\n\n }, function(err) {\n alertService.add({msg: \"Unable to fetch vendor accounts for \" + newVal + \" branch\", type: \"error\"});\n });\n $scope.refreshOrderLines();\n\n }\n });\n\n $scope.$watch('subscription.vendor_account_id', function(newVal, oldVal) {\n if (newVal) {\n $scope.vendor_accounts.forEach(function(va) {\n if (va.id == newVal) {\n $scope.subscription.vendor_account_name = va.name;\n }\n });\n if (newVal !== oldVal) {\n console.log(\"Loading POs for VAID=\" + newVal);\n $scope.subscription.purchase_order_number = null;\n $scope.subscription.purchase_order_line_id = null;\n kwApi.Branch.getAcqPurchaseOrders({\n id: $scope.subscription.branch_code,\n view: 'details',\n vendor_account_id: $scope.subscription.vendor_account_id,\n }).$promise.then(function(rv) {\n $scope.purchase_orders = rv;\n });\n }\n }\n });\n\n $scope.$watch('subscription.purchase_order_number', function(newVal, oldVal) {\n if (newVal && (newVal !== oldVal)) {\n $scope.subscription.purchase_order_line_id = null;\n kwApi.Branch.getAcqPurchaseOrderLines({\n id: $scope.subscription.branch_code,\n number: $scope.subscription.purchase_order_number\n }).$promise.then(function(rv) {\n $scope.purchase_order_lines = rv;\n });\n }\n });\n\n\n $scope.periodical = kwApi.Periodical.get({id: $scope.subscription.periodical_id});\n\n $scope.vendorAccountSelectConfig = {\n maxItems: 1,\n valueField: 'id',\n labelField: 'name',\n searchField: ['name', 'vendor_name'],\n render: {\n item: function(item, escape) {\n if (!item) return '';\n var t = '
    ' + escape(item.name) + ' (' + escape(item.vendor_name) + ')
    ';\n return t;\n },\n option: function(item, escape) {\n if (!item) return '';\n var t = '
    ' + escape(item.name) + ' (' + escape(item.vendor_name) + ')
    ';\n return t;\n }\n },\n\n };\n\n $scope.branchSelectConfig = {\n maxItems: 1,\n valueField: 'branchcode',\n labelField: 'branchname',\n searchField: ['branchname','branchcode'],\n };\n\n $scope.purchaseOrderSelectConfig = {\n maxItems: 1,\n valueField: 'number',\n labelField: 'number',\n searchField: ['number'],\n render: {\n item: function(item, escape) {\n if (!item) return '';\n return '
    ' + item.number + ' (' + item.vendor_account_name + ')
    ';\n },\n option: function(item, escape) {\n if (!item) return '';\n return '
    ' + item.number + ' (' + item.vendor_account_name + ')
    ';\n }\n }\n };\n\n $scope.purchaseOrderLineSelectConfig = {\n maxItems: 1,\n valueField: 'id',\n labelField: 'title',\n searchField: ['title'],\n render: {\n item: function(item, escape) {\n if (!item) return '';\n return '
    ' + item.title + ' (#' + item.id + ', ' + item.copies_count + ' copies)
    ';\n },\n option: function(item, escape) {\n if (!item) return '';\n return '
    ' + item.title + ' (#' + item.id + ', ' + item.copies_count + ' copies)
    ';\n }\n }\n };\n\n $scope.pairPurchaseOrder = function() {\n kwApi.Branch.pairAcqSubscriptionPOLine({id: $scope.subscription.branch_code, subscription_id: $scope.subscription.id, purchase_order_line_id: $scope.subscription.purchase_order_line_id},{}).$promise.then(function(rv) {\n alertService.add({msg: \"Paired successfully\", type: \"info\"});\n $scope.refreshOrderLines();\n }, function(err) {\n alertService.addApiError(err, 'Unable to pair');\n });\n };\n\n $scope.subscriptionEdit = function() {\n $scope.orig = angular.copy($scope.subscription);\n $scope.dirty = false;\n $scope.editing.main = true;\n $scope.expand._prev_item = $scope.expand.item;\n $scope.expand.item = true;\n $scope.expand._prev_rlist = $scope.expand.rlist;\n $scope.expand.rlist = true;\n if ($scope.orderLinePairing.mode == 'override') {\n $scope.subscription.purchase_order_number = $scope.orderLinePairing.overrideOrder;\n $scope.orderLinePairing.mode = 'paired';\n }\n };\n\n $scope.subscriptionEditCancel = function() {\n if ($scope.subscription.id) {\n $scope.dirty = false;\n angular.extend($scope.subscription,$scope.orig);\n $scope.editing.main = false;\n $scope.expand.item = $scope.expand._prev_item;\n $scope.expand.rlist = $scope.expand._prev_rlist;\n }\n else {\n $state.go('staff.serials.periodical', {id: $scope.subscription.periodical_id});\n }\n };\n\n \n $scope.subscriptionEditSave = function() {\n loading.add();\n\n var isNew = false; \n $scope.subscription.start_date = $scope.subscription._obj_start_date ?\n dayjs($scope.subscription._obj_start_date).startOf('day').format('YYYY-MM-DD HH:mm:ss') : null;\n $scope.subscription.expiration_date = $scope.subscription._obj_expiration_date ?\n dayjs($scope.subscription._obj_expiration_date).startOf('day').format('YYYY-MM-DD HH:mm:ss') : null;\n\n var promise;\n if ($scope.subscription.id) {\n promise = kwApi.Subscription.put({id: $scope.subscription.id}, $scope.subscription).$promise;\n }\n else {\n promise = kwApi.Subscription.save($scope.subscription).$promise;\n isNew = true;\n }\n\n promise.then(function(rv) {\n if (isNew) {\n return $q.when(rv);\n }\n else {\n return kwApi.Subscription.get({id: $scope.subscription.id}).$promise;\n }\n }).then(function(obj) {\n if (isNew) {\n $state.go('staff.serials.subscription', {id: obj.id});\n }\n angular.extend($scope.subscription,obj);\n $scope.editing.main = false;\n $scope.expand.item = $scope.expand._prev_item;\n $scope.dirty = false;\n loading.resolve();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err, 'Save failed');\n });\n };\n\n $scope.$watch('subscription', function(newVal, oldVal) {\n if (newVal && oldVal && !angular.equals(newVal, oldVal)) {\n $scope.dirty = true;\n }\n }, true);\n\n $scope.defaultFields = [];\n $scope.item = $scope.subscription.item_defaults;\n\n $scope.itemFields = configService.ItemFields;\n $scope.itemFields.forEach(function(f) {\n if (f.code in subscription.item_defaults) {\n $scope.defaultFields.push(angular.extend({}, f, {\n custom: false, // just flatten everything, we'll apply accordingly later\n }));\n }\n });\n $scope.availableItemFields = $scope.itemFields;\n $scope.$watch('item', function(newVal) {\n if (!newVal) return;\n $scope.availableItemFields = $scope.itemFields.filter(function(f) {\n return ((f.code in newVal) ? false : true);\n });\n }, true);\n\n $scope.itemDefaultAdd = function(newField) {\n if (newField) {\n $scope.itemFields.forEach(function(f) {\n if (f.code == newField) {\n $scope.defaultFields.push(angular.extend({}, f, {\n custom: false,\n }));\n if (f.code == 'wthdrawn' || f.code == 'restricted' || f.code == 'notforloan')\n $scope.item[f.code] = 0;\n else\n $scope.item[f.code] = '';\n }\n });\n $scope.subscription._newItemField = undefined;\n }\n };\n\n $scope.$watch('subscription._newItemField', function(newVal) {\n if (newVal) {\n $scope.itemDefaultAdd($scope.subscription._newItemField);\n }\n });\n\n $scope.itemDefaultDel = function(idx) {\n delete $scope.item[$scope.defaultFields[idx].code];\n $scope.defaultFields.splice(idx,1);\n };\n\n\n}])\n.controller('StaffSerialsSubscriptionReceiveCtrl', [\"$scope\", \"subscriptions\", \"serialInstances\", \"kwApi\", \"$filter\", function($scope, subscriptions, serialInstances, kwApi, $filter) {\n $scope.subscriptions = subscriptions;\n $scope.subscription = true;\n\n $scope.editing = {};\n\n var byId = {};\n angular.forEach(subscriptions, function(s) {\n byId[s.id] = s;\n });\n\n var instances = [];\n angular.forEach(serialInstances, function(lst) {\n angular.forEach(lst, function(e) {\n e.subscription_name = byId[e.subscription_id].name || ('#'+e.subscription_id);\n if (e.expected_date) e._obj_expected_date = dayjs(e.expected_date).toDate();\n instances.push(e);\n });\n });\n\n $scope.serialInstances = instances.sort(function(a,b) {\n var aDate = a.expected_date || a.publication_date;\n var bDate = b.expected_date || b.publication_date;\n if (aDate < bDate) return 1;\n else if (aDate > bDate) return -1;\n else return 0;\n });\n\n $scope.periodical = kwApi.Periodical.get({id: $scope.subscriptions[0].periodical_id});\n\n var names = [];\n $scope.subscriptions.forEach(function(s) {\n var notes = [];\n if (s.staff_note) {\n notes.push($scope.subscription.staff_note);\n }\n if (s.opac_note) {\n notes.push($scope.subscription.opac_note);\n }\n\n if (s.adds_items && s.adds_po_lines) {\n notes.push(\"Receiving creates items and PO lines\");\n }\n else if (s.adds_items) {\n notes.push(\"Receiving creates items but not PO lines\");\n }\n else if (s.adds_po_lines) {\n notes.push(\"Receiving creates PO lines\");\n }\n else {\n notes.push(\"Receiving does not create items or PO lines\");\n }\n s.notes = notes.join(' / ');\n\n names.push(s.name || $filter('displayName')(s.branch_code, 'branch'));\n });\n\n $scope.names = names.join(', ');\n}])\n\n.controller('StaffSerialsEditionCtrl', [\"$scope\", \"serialEdition\", \"serialInstances\", \"branches\", \"kwApi\", \"loading\", \"alertService\", \"$q\", \"$state\", function($scope, serialEdition, serialInstances, branches, kwApi, loading, alertService, $q, $state) {\n $scope.serialEdition = serialEdition;\n $scope.serialInstances = serialInstances;\n $scope.branches = branches.filter(function(b) {\n return $scope.userCanAccessBranch(b.branchcode);\n }).sort(function(a,b) {\n return (a.branchname < b.branchname ? -1 : a.branchname > b.branchname ? 1 : 0)\n });\n\n $scope.periodical = kwApi.Periodical.get({id: $scope.serialEdition.periodical_id});\n\n $scope.editing = {main: false};\n $scope.expand = {};\n\n $scope.serialEdition._obj_publication_date = dayjs($scope.serialEdition.publication_date).toDate();\n\n $scope.serialEditionEdit = function() {\n $scope.orig = angular.copy($scope.serialEdition);\n $scope.dirty = false;\n $scope.editing.main = true;\n $scope.expand._prev_copies = $scope.expand.copies;\n $scope.expand.copies = true;\n };\n\n $scope.serialEditionEditCancel = function() {\n $scope.dirty = false;\n angular.extend($scope.serialEdition,$scope.orig);\n $scope.editing.main = false;\n $scope.expand.copies = $scope.expand._prev_copies;\n };\n\n \n $scope.serialEditionEditSave = function() {\n loading.add();\n\n var isNew = false; \n $scope.serialEdition.publication_date = $scope.serialEdition._obj_publication_date ?\n dayjs($scope.serialEdition._obj_publication_date).startOf('day').format('YYYY-MM-DD HH:mm:ss') : null;\n\n var promise;\n if ($scope.serialEdition.id) {\n promise = kwApi.SerialEdition.put({id: $scope.serialEdition.id}, $scope.serialEdition).$promise;\n }\n else {\n promise = kwApi.SerialEdition.save($scope.serialEdition).$promise;\n isNew = true;\n }\n\n promise.then(function(rv) {\n if (isNew) {\n return $q.when(rv);\n }\n else {\n return kwApi.SerialEdition.get({id: $scope.serialEdition.id}).$promise;\n }\n }).then(function(obj) {\n if (isNew) {\n $state.go('staff.serials.edition', {id: obj.id});\n }\n angular.extend($scope.serialEdition,obj);\n $scope.editing.main = false;\n $scope.expand.copies = $scope.expand._prev_copies;\n $scope.dirty = false;\n loading.resolve();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err, 'Save failed');\n });\n };\n\n $scope.$watch('serialEdition', function(newVal, oldVal) {\n if (newVal && oldVal && !angular.equals(newVal, oldVal)) {\n $scope.dirty = true;\n }\n }, true);\n}])\n\n.controller('StaffSerialsInstancesCtrl', [\"$scope\", \"kwApi\", \"loading\", \"alertService\", \"$uibModal\", \"bibService\", \"bvPrintSvc\", function($scope, kwApi, loading, alertService, $uibModal, bibService, bvPrintSvc) {\n var disableWatch, enableWatch;\n\n if ($scope.subscription) {\n $scope.instancesOrder = {field: 'publication_date', reverse: true};\n }\n else {\n $scope.instancesOrder = {field: 'branch_name', reverse: false};\n }\n\n // FIXME - this could really be a separate controller, or even better, a directive\n\n $scope.statuses = [\n { id: '1', name: 'Expected' },\n { id: '2', name: 'Arrived' },\n { id: '3', name: 'Late' },\n { id: '4', name: 'Missing' },\n { id: '5', name: 'Unavailable' },\n { id: '6', name: 'Deleted' },\n { id: '7', name: 'Claimed' },\n ];\n\n\n $scope.serialInstances.forEach(function(e) {\n if (e.expected_date) e._obj_expected_date = dayjs(e.expected_date).toDate();\n if (e.received_date) e._obj_received_date = dayjs(e.received_date).toDate();\n });\n\n $scope.serialInstancesEdit = function() {\n $scope.origIssues = angular.copy($scope.serialInstances);\n $scope.copiesDirty = false;\n $scope.editing.copies = true;\n };\n\n $scope.serialInstancesEditCancel = function() {\n $scope.dirty = false;\n $scope.serialInstances = $scope.origIssues;\n $scope.editing.copies = false;\n };\n\n $scope.openEditModal = function(holdings,item_id) {\n $uibModal.open({\n backdrop: false,\n templateUrl: '/app/static/partials/staff/item-edit-modal.html',\n controller: 'StaffItemEditCtrl',\n resolve: {\n holdings: function() { return holdings; },\n itemid: function() { return item_id; },\n mfhdid: function() { return null; },\n clone: function() { return null; },\n }\n });\n };\n\n $scope.serialInstancesPrintRouting = function() {\n var toPrint = $scope.serialInstances.filter(function(e) { return e.received });\n bvPrintSvc.print({\n templateUrl: '/app/static/partials/staff/serials/routing-list-print-all.html',\n controller: function($scope) {\n $scope.issues = toPrint;\n }\n });\n };\n\n \n $scope.serialInstancesEditSave = function() {\n loading.add();\n\n $scope.serialInstances.forEach(function(e) {\n e.expected_date = e._obj_expected_date ? dayjs(e._obj_expected_date).startOf('day').format('YYYY-MM-DD HH:mm:ss') : null;\n e.received_date = e._obj_received_date ? dayjs(e._obj_received_date).startOf('day').format('YYYY-MM-DD HH:mm:ss') : null;\n if (e.received) {\n e.status = 2;\n }\n\n });\n\n var promise;\n\n disableWatch();\n kwApi.SerialInstance.saveAll($scope.serialInstances).$promise.then(function(rv) {\n $scope.serialInstances = rv;\n var newItems = [];\n var bib_id;\n for (var i=0; i b.branchname ? 1 : 0)\n });\n $scope.branch_code = branches[0].branchcode;\n $scope.vendor_accounts = [];\n $scope.vendor_account_id = undefined;\n $scope.instancesOrder = {field: 'publication_date', reverse: false};\n $scope.serialInstances = [];\n $scope.vendor_emails = '';\n\n $scope.$watch('branch_code', function(newVal) {\n if (newVal) {\n $scope.vendor_account_id = undefined;\n kwApi.Branch.getAcqVendorAccounts({id: newVal}).$promise.then(function(rv) {\n $scope.vendor_accounts = rv;\n }, function(err) {\n alertService.add({msg: \"Unable to fetch vendor accounts for \" + newVal + \" branch\", type: \"error\"});\n });\n }\n });\n\n $scope.$watch('vendor_account_id', function(newVal) {\n if (newVal && $scope.branch_code) {\n\n $scope.vendor_accounts.forEach(function(va) {\n if (va.id != newVal) return;\n\n var bestEmail, altEmail, fallbackEmail;\n var bestAddr, altAddr, fallbackAddr;\n va.addresses.forEach(function(a) {\n if (a.email) {\n if (!fallbackEmail)\n fallbackEmail = a.email;\n a.use_names.forEach(function(u) {\n if (u == 'claims') {\n bestEmail = a.email;\n }\n else if (u == 'all') {\n if (!bestEmail) bestEmail = a.email;\n }\n else {\n if (!altEmail) altEmail = a.email;\n }\n });\n }\n if (a.line_1) {\n if (!fallbackAddr)\n fallbackAddr = a;\n a.use_names.forEach(function(u) {\n if (u == 'claims') {\n bestAddr = a;\n }\n else if (u == 'all') {\n if (!bestAddr) bestAddr = a;\n }\n else {\n if (!altAddr) altAddr = a;\n }\n });\n }\n\n });\n $scope.vendor_emails = bestEmail || altEmail || fallbackEmail || '';\n $scope.address = bestAddr || altAddr || fallbackAddr || {};\n });\n\n kwApi.Branch.getSerialInstances({id: $scope.branch_code, scope: 'claimable', vendor_account_id: newVal, include_claimed: ($scope.include_claimed ? 1 : 0)}).$promise.then(function(rv) {\n $scope.serialInstances = rv;\n }, function(err) {\n alertService.add({msg: \"Unable to fetch issues for this branch and vendor\", type: \"error\"});\n });\n }\n else {\n $scope.serialInstances = [];\n }\n });\n\n $scope.$watch('include_claimed', function(newVal) {\n if ($scope.branch_code && $scope.vendor_account_id) {\n kwApi.Branch.getSerialInstances({id: $scope.branch_code, scope: 'claimable', vendor_account_id: $scope.vendor_account_id, include_claimed: ($scope.include_claimed ? 1 : 0)}).$promise.then(function(rv) {\n $scope.serialInstances = rv;\n }, function(err) {\n alertService.add({msg: \"Unable to fetch issues for this branch and vendor\", type: \"error\"});\n });\n }\n });\n\n $scope.vendorAccountSelectConfig = {\n maxItems: 1,\n valueField: 'id',\n labelField: 'name',\n searchField: ['name', 'vendor_name'],\n render: {\n item: function(item, escape) {\n if (!item) return '';\n var t = '
    ' + escape(item.name) + ' (' + escape(item.vendor_name) + ')
    ';\n return t;\n },\n option: function(item, escape) {\n if (!item) return '';\n var t = '
    ' + escape(item.name) + ' (' + escape(item.vendor_name) + ')
    ';\n return t;\n }\n },\n\n };\n\n $scope.branchSelectConfig = {\n maxItems: 1,\n valueField: 'branchcode',\n labelField: 'branchname',\n searchField: ['branchname','branchcode'],\n };\n\n $scope.submitClaim = function() {\n var claimed = $scope.serialInstances.filter(function(si) {\n return si.selected;\n }).map(function(rv) {\n return { id: rv.id };\n });\n if (!claimed.length) return;\n\n kwApi.SerialInstance.claim({email: $scope.vendor_emails, mark_claimed: ($scope.mark_claimed ? 1 : 0)},claimed).$promise.then(function(rv) {\n alertService.add({msg: \"Claim email sent\", type: \"success\"});\n kwApi.Branch.getSerialInstances({id: $scope.branch_code, scope: 'claimable', vendor_account_id: $scope.vendor_account_id, include_claimed: ($scope.include_claimed ? 1 : 0)}).$promise.then(function(rv) {\n $scope.serialInstances = rv;\n }, function(err) {\n alertService.add({msg: \"Unable to fetch issues for this branch and vendor\", type: \"error\"});\n });\n }, function(err) {\n alertService.addApiError(err, 'Unable to submit claim');\n });\n };\n\n $scope.printClaim = function() {\n var claimed = $scope.serialInstances.filter(function(si) {\n return si.selected;\n }).map(function(rv) {\n return { id: rv.id };\n });\n if (!claimed.length) return;\n var addr = $scope.address;\n\n kwApi.SerialInstance.claim({print: 1, mark_claimed: ($scope.mark_claimed ? 1 : 0)},claimed).$promise.then(function(rv) {\n bvPrintSvc.print({\n templateUrl: '/app/static/partials/staff/serials/claim-print.html',\n controller: function($scope) {\n $scope.rendered = rv.rendered;\n $scope.address = addr;\n }\n });\n\n kwApi.Branch.getSerialInstances({id: $scope.branch_code, scope: 'claimable', vendor_account_id: $scope.vendor_account_id, include_claimed: ($scope.include_claimed ? 1 : 0)}).$promise.then(function(rv) {\n $scope.serialInstances = rv;\n }, function(err) {\n alertService.add({msg: \"Unable to fetch issues for this branch and vendor\", type: \"error\"});\n });\n }, function(err) {\n alertService.addApiError(err, 'Unable to submit claim');\n });\n };\n\n}])\n\n\n.controller('StaffSerialsExpiresCtrl', [\"$scope\", \"branches\", \"kwApi\", \"loading\", \"$uibModal\", function($scope, branches, kwApi, loading, $uibModal) {\n $scope.branches = branches.filter(function(b) {\n return $scope.userCanAccessBranch(b.branchcode);\n }).sort(function(a,b) {\n return (a.branchname < b.branchname ? -1 : a.branchname > b.branchname ? 1 : 0)\n });\n $scope.branch_code = branches[0].branchcode;\n\n $scope.subscriptionsOrder = {field: 'expiration_date', reverse: false};\n $scope.subscriptions = [];\n $scope.since = '1 MONTH';\n $scope.sinceOptions = [\n {label: \"1 month\", value: \"1 MONTH\"},\n {label: \"3 months\", value: \"3 MONTH\"},\n {label: \"6 months\", value: \"6 MONTH\"},\n {label: \"1 year\", value: \"12 MONTH\"},\n ];\n\n $scope.reload = function() {\n loading.wrap(\n kwApi.Branch.getSubscriptions({id: $scope.branch_code, since: $scope.since}).$promise, \n \"Unable to fetch subscriptions for \" + $scope.branch_code + \" branch\"\n ).then(function(rv) {\n $scope.subscriptions = rv;\n });\n };\n\n $scope.$watch('branch_code', function(newVal) {\n if (newVal) {\n $scope.reload();\n }\n });\n\n $scope.$watch('since', function(newVal) {\n if (newVal) {\n $scope.reload();\n }\n });\n\n $scope.branchSelectConfig = {\n maxItems: 1,\n valueField: 'branchcode',\n labelField: 'branchname',\n searchField: ['branchname','branchcode'],\n };\n\n $scope.renew = function(c) {\n var newStart, newEnd;\n if (c.start_date && c.expiration_date) {\n var sdate = dayjs(c.start_date);\n var edate = dayjs(c.expiration_date);\n var daysDiff = edate.diff(sdate, 'days');\n newStart = dayjs(edate);\n newStart.add(1, 'days');\n newEnd = dayjs(newStart);\n newEnd.add(daysDiff, 'days');\n }\n else if (c.expiration_date) {\n newEnd = dayjs(c.expiration_date).add(1, 'years');\n }\n else if (c.start_date) {\n newStart = dayjs(c.start_date).add(1, 'years');\n }\n\n if (newStart)\n c.new_start_date = newStart.toDate();\n if (newEnd)\n c.new_expiration_date = newEnd.toDate();\n\n $uibModal.open({\n backdrop: false,\n templateUrl: '/app/static/partials/staff/serials/renew-modal.html',\n controller: [\"$scope\", \"subscription\", function($scope, subscription) {\n $scope.startOpened = false;\n $scope.dateFormat = \"MM/dd/yyyy\";\n $scope.subscription = subscription;\n $scope.save = function() {\n $scope.subscription.start_date = $scope.subscription.new_start_date ?\n dayjs($scope.subscription.new_start_date).startOf('day').format('YYYY-MM-DD HH:mm:ss') : null;\n $scope.subscription.expiration_date = $scope.subscription.new_expiration_date ?\n dayjs($scope.subscription.new_expiration_date).startOf('day').format('YYYY-MM-DD HH:mm:ss') : null;\n\n\n loading.wrap(kwApi.Subscription.put({id: $scope.subscription.id}, $scope.subscription).$promise).then(function(rv) {\n $scope.$close(1);\n });\n };\n }],\n resolve: {\n subscription: function() { return c; },\n },\n }).result.then(function(rv) {\n if (rv)\n $scope.reload();\n });\n };\n \n\n}])\n \n.controller('StaffSerialsScheduleTemplatesCtrl', [\"$scope\", \"templates\", \"kwApi\", \"alertService\", \"kohaDlg\", \"$uibModal\", function($scope, templates, kwApi, alertService, kohaDlg, $uibModal) {\n $scope.templates = templates;\n $scope.templates.forEach(function(t) {\n t._is_basic = (t.patterns.length == 1 && !t.chronology_template && !t.sequence_template);\n t.pattern = (t.patterns[0] || {}).pattern;\n });\n\n $scope.showHelp = function(s) {\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/serials/help-' + s + '.html',\n size: 'md'\n });\n };\n $scope.refresh = function() {\n kwApi.SerialPatternTemplate.query().$promise.then(function(rv) {\n $scope.templates = rv;\n $scope.templates.forEach(function(t) {\n t._is_basic = (t.patterns.length == 1 && !t.chronology_template && !t.sequence_template);\n if (!t.patterns) t.patterns = [];\n t.pattern = (t.patterns[0] || {}).pattern;\n });\n });\n };\n\n $scope.deleteRow = function(n) {\n kohaDlg.dialog({\n heading: 'Are you sure?',\n message: 'Are you sure you wish to delete this template? This cannot be undone.',\n buttons: [{val: true, label: 'Yes', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n }).result.then(function(rv) {\n if (rv) {\n kwApi.SerialPatternTemplate.delete({id: $scope.templates[n].id}).$promise.then(function(rv) {\n $scope.templates.splice(n,1);\n }, function(err) {\n alertService.addApiError(err, 'Delete failed');\n });\n }\n });\n };\n\n $scope.editingAny = false;\n\n $scope.editRow = function(n) {\n $scope.templates[n].editing = true;\n $scope.editingAny = true;\n };\n\n $scope.cancelEdit = function(n) {\n $scope.templates[n].editing = false;\n $scope.editingAny = false;\n $scope.refresh();\n };\n\n $scope.newRow = function() {\n $scope.templates.push({\n editing: true,\n patterns: [{\n pattern: '1*0:0:0:0:0:0',\n start_date: '01-01',\n end_date: '12-31',\n chronology_template: null,\n sequence_template: null\n }],\n name: '',\n description: '',\n pattern: '1*0:0:0:0:0:0',\n _is_basic: true\n });\n $scope.editingAny = true;\n };\n\n $scope.saveEdit = function(n) {\n var row = $scope.templates[n];\n var promise;\n if (row._is_basic) {\n row.patterns = JSON.stringify([{\n pattern: row.pattern,\n start_date: '01-01',\n end_date: '12-31',\n chronology_template: null,\n sequence_template: null,\n }]);\n }\n if (row.id) {\n promise = kwApi.SerialPatternTemplate.put({id: row.id}, row).$promise;\n }\n else {\n promise = kwApi.SerialPatternTemplate.save(row).$promise;\n }\n promise.then(function(rv) {\n $scope.refresh();\n $scope.editingAny = false;\n }, function(err) {\n alertService.addApiError(err, 'Save failed');\n });\n };\n\n $scope.editAdvanced = function(n) {\n $uibModal.open({\n backdrop: false,\n templateUrl: '/app/static/partials/staff/serials/schedule-edit-advanced.html',\n controller: [\"$scope\", \"patterns\", function($scope, patterns) {\n $scope.patterns = patterns;\n $scope.patterns.forEach(function(p) {\n if (!p.chronology_template || p.chronology_template === null) p.chronology_template = '';\n if (!p.sequence_template || p.sequence_template === null) p.sequence_template = '';\n });\n $scope.saved = angular.copy($scope.patterns);\n\n $scope.editingAny = false;\n\n $scope.showHelp = function(s) {\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/serials/help-' + s + '.html',\n size: 'md'\n });\n };\n\n $scope.newRow = function(n) {\n $scope.patterns.push({\n pattern: '1*0:0:0:0:0:0',\n start_date: '01-01',\n end_date: '12-31',\n chronology_template: null,\n sequence_template: null,\n editing: true,\n });\n $scope.editingAny = true;\n };\n $scope.editRow = function(n) {\n $scope.editingAny = true;\n $scope.patterns[n].editing = true;\n $scope.saved[n] = angular.copy($scope.patterns[n]);\n };\n $scope.deleteRow = function(n) {\n $scope.patterns.splice(n,1);\n };\n $scope.saveEdit = function(n) {\n $scope.patterns[n].editing = false;\n $scope.editingAny = false;\n };\n $scope.cancelEdit = function(n) {\n if ($scope.saved[n]) {\n $scope.patterns[n] = $scope.saved[n];\n $scope.patterns[n].editing = false;\n }\n else {\n $scope.patterns.pop();\n }\n $scope.saved[n] = null;\n $scope.editingAny = false;\n };\n }],\n size: 'lg',\n resolve: {\n patterns: function() { return $scope.templates[n].patterns; },\n }\n }).result.then(function() {\n $scope.templates[n].patterns.forEach(function(p) {\n if (!p.chronology_template || /^\\s*$/.test(p.chronology_template)) p.chronology_template = null;\n if (!p.sequence_template || /^\\s*$/.test(p.sequence_template)) p.sequence_template = null;\n });\n });\n\n };\n\n}])\n\n\n.controller('StaffSerialsChronologyTemplatesCtrl', [\"$scope\", \"templates\", \"kwApi\", \"alertService\", \"kohaDlg\", \"$uibModal\", function($scope, templates, kwApi, alertService, kohaDlg, $uibModal) {\n $scope.templates = templates;\n\n $scope.showHelp = function(s) {\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/serials/help-' + s + '.html',\n size: 'md'\n });\n };\n $scope.refresh = function() {\n kwApi.SerialChronologyTemplate.query().$promise.then(function(rv) {\n $scope.templates = rv;\n });\n };\n\n $scope.deleteRow = function(n) {\n kohaDlg.dialog({\n heading: 'Are you sure?',\n message: 'Are you sure you wish to delete this template? This cannot be undone.',\n buttons: [{val: true, label: 'Yes', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n }).result.then(function(rv) {\n if (rv) {\n kwApi.SerialChronologyTemplate.delete({id: $scope.templates[n].id}).$promise.then(function(rv) {\n $scope.templates.splice(n,1);\n }, function(err) {\n alertService.addApiError(err, 'Delete failed');\n });\n }\n });\n };\n\n $scope.editingAny = false;\n\n $scope.editRow = function(n) {\n $scope.editingAny = true;\n $scope.templates[n].editing = true;\n };\n\n $scope.newRow = function() {\n $scope.editingAny = true;\n $scope.templates.push({editing: true, name: '', description: '', pattern: ''});\n };\n\n $scope.saveEdit = function(n) {\n var row = $scope.templates[n];\n var promise;\n if (row.id) {\n promise = kwApi.SerialChronologyTemplate.put({id: row.id}, row).$promise;\n }\n else {\n promise = kwApi.SerialChronologyTemplate.save(row).$promise;\n }\n promise.then(function(rv) {\n $scope.refresh();\n $scope.editingAny = false;\n }, function(err) {\n alertService.addApiError(err, 'Save failed');\n });\n };\n\n $scope.cancelEdit = function(n) {\n $scope.templates[n].editing = false;\n $scope.editingAny = false;\n $scope.refresh();\n };\n}])\n\n\n.controller('StaffSerialsSequenceTemplatesCtrl', [\"$scope\", \"templates\", \"kwApi\", \"alertService\", \"kohaDlg\", \"$uibModal\", function($scope, templates, kwApi, alertService, kohaDlg, $uibModal) {\n $scope.templates = templates;\n\n $scope.refresh = function() {\n kwApi.SerialSequenceTemplate.query().$promise.then(function(rv) {\n $scope.templates = rv;\n });\n };\n $scope.showHelp = function(s) {\n $uibModal.open({\n templateUrl: '/app/static/partials/staff/serials/help-' + s + '.html',\n size: 'md'\n });\n };\n\n $scope.deleteRow = function(n) {\n kohaDlg.dialog({\n heading: 'Are you sure?',\n message: 'Are you sure you wish to delete this template? This cannot be undone.',\n buttons: [{val: true, label: 'Yes', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n }).result.then(function(rv) {\n if (rv) {\n kwApi.SerialSequenceTemplate.delete({id: $scope.templates[n].id}).$promise.then(function(rv) {\n $scope.templates.splice(n,1);\n }, function(err) {\n alertService.addApiError(err, 'Delete failed');\n });\n }\n });\n };\n\n $scope.editingAny = false;\n\n $scope.editRow = function(n) {\n $scope.templates[n].editing = true;\n $scope.editingAny = true;\n };\n\n $scope.newRow = function() {\n $scope.templates.push({editing: true, name: '', description: '', pattern: ''});\n $scope.editingAny = true;\n };\n\n $scope.saveEdit = function(n) {\n var row = $scope.templates[n];\n var promise;\n if (row.id) {\n promise = kwApi.SerialSequenceTemplate.put({id: row.id}, row).$promise;\n }\n else {\n promise = kwApi.SerialSequenceTemplate.save(row).$promise;\n }\n promise.then(function(rv) {\n $scope.refresh();\n $scope.editingAny = false;\n }, function(err) {\n alertService.addApiError(err, 'Delete failed');\n });\n };\n\n $scope.cancelEdit = function(n) {\n $scope.templates[n].editing = false;\n $scope.editingAny = false;\n $scope.refresh();\n };\n}])\n\n.controller('StaffSerialsVendorsCtrl', [\"$scope\", \"kwApi\", \"loading\", \"alertService\", \"branches\", \"$uibModal\", \"kohaDlg\", function($scope, kwApi, loading, alertService, branches, $uibModal, kohaDlg) {\n $scope.branches = branches.filter(function(b) {\n return $scope.userCanAccessBranch(b.branchcode);\n }).sort(function(a,b) {\n return (a.branchname < b.branchname ? -1 : a.branchname > b.branchname ? 1 : 0)\n });\n\n $scope.branchSelectConfig = {\n maxItems: 1,\n valueField: 'branchcode',\n labelField: 'branchname',\n searchField: ['branchname','branchcode'],\n };\n\n $scope.reloadVendorAccounts = function() {\n loading.add();\n kwApi.Branch.getAcqVendorAccounts({id: $scope.branch_code}).$promise.then(function(vendor_accounts) {\n vendor_accounts.forEach(function(va) {\n va._is_serials_vendor = (va.internal_vendor_account_number == 'CREATED BY SERIALS');\n\n var bestEmail, altEmail, fallbackEmail;\n var bestAddr, altAddr, fallbackAddr;\n va.addresses.forEach(function(a) {\n\n if (a.email) {\n if (!fallbackEmail)\n fallbackEmail = a.email;\n a.use_names.forEach(function(u) {\n if (u == 'claims') {\n bestEmail = a.email;\n }\n else if (u == 'all') {\n if (!bestEmail) bestEmail = a.email;\n }\n else {\n if (!altEmail) altEmail = a.email;\n }\n });\n }\n if (a.line_1) {\n if (!fallbackAddr)\n fallbackAddr = a;\n a.use_names.forEach(function(u) {\n if (u == 'claims') {\n bestAddr = a;\n }\n else if (u == 'all') {\n if (!bestAddr) bestAddr = a;\n }\n else {\n if (!altAddr) altAddr = a;\n }\n });\n }\n });\n va.best_email = bestEmail || altEmail || fallbackEmail;\n va.best_address = bestAddr || altAddr || fallbackAddr;\n });\n\n loading.resolve();\n $scope.vendor_accounts = vendor_accounts;\n }, function(err) {\n loading.resolve();\n alertService.add({msg: \"Unable to fetch vendor accounts for \" + $scope.branch_code + \" branch\", type: \"error\"});\n });\n };\n\n $scope.$watch('branch_code', function(newVal) {\n if (newVal) {\n $scope.reloadVendorAccounts();\n }\n });\n\n $scope.editVendor = function(vendor_account) {\n var vendorIdx = {};\n $scope.vendor_accounts.forEach(function(va) {\n vendorIdx[va.vendor_name] = va.vendor_id;\n });\n /*var vendors = [];\n Object.keys(vendorIdx).sort().forEach(function(name) {\n vendors.push({name: name, id: vendorIdx[name]});\n });*/\n\n\n $uibModal.open({\n backdrop: true,\n templateUrl: '/app/static/partials/staff/serials/vendor-account-edit.html',\n controller: 'StaffSerialsVendorEditCtrl',\n size: 'lg',\n resolve: {\n vendor_account: function() { return angular.copy(vendor_account); },\n countries: function() { \n return kwApi.Branch.getAcqVendorCountries({id: $scope.branch_code }).$promise;\n },\n vendors: function() {\n return kwApi.Branch.getAcqVendors({id: $scope.branch_code}).$promise;\n },\n branch_code: function() { return $scope.branch_code; },\n }\n }).result.then(function(rv) {\n $scope.reloadVendorAccounts();\n });\n };\n\n $scope.newVendor = function() {\n $scope.editVendor({\n addresses: [{}],\n });\n };\n\n $scope.deleteVendor = function(vendor_account) {\n kohaDlg.dialog({\n heading: 'Are you sure?',\n message: 'Are you sure you wish to delete this vendor account? This cannot be undone.',\n buttons: [{val: true, label: 'Yes', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n }).result.then(function(rv) {\n if (rv) {\n kwApi.Branch.deleteAcqVendorAccount({id: $scope.branch_code, vendor_account_id: vendor_account.id}, {}).$promise.then(function() {\n $scope.reloadVendorAccounts();\n }, function(err) {\n alertService.addApiError(err, 'Delete failed');\n });\n }\n });\n };\n\n}])\n\n.controller('StaffSerialsVendorEditCtrl', [\"$scope\", \"kwApi\", \"vendor_account\", \"vendors\", \"countries\", \"alertService\", \"branch_code\", function ($scope, kwApi, vendor_account, vendors, countries, alertService, branch_code) {\n $scope.vendor_account = vendor_account;\n $scope.is_new = ($scope.vendor_account.id ? false : true);\n\n $scope.vendors = vendors;\n $scope.countries = countries;\n $scope.branch_code = branch_code;\n\n if ($scope.is_new) {\n $scope.countries.forEach(function(c) {\n if (c.name === 'United States') {\n $scope.vendor_account.addresses[0].country_id = c.id;\n }\n });\n }\n \n $scope.vendorSelectConfig = {\n create: true,\n maxItems: 1,\n valueField: 'name',\n labelField: 'name',\n searchField: ['name'],\n };\n\n $scope.countrySelectConfig = {\n maxItems: 1,\n valueField: 'id',\n labelField: 'name',\n searchField: ['name'],\n };\n\n $scope.save = function() {\n if ($scope.is_new) {\n $scope.vendors.forEach(function(v) {\n if (v.name === $scope.vendor_account.vendor_name) {\n $scope.vendor_account.vendor_id = v.id;\n }\n });\n }\n\n kwApi.Branch.saveAcqVendorAccount({id: $scope.branch_code}, $scope.vendor_account).$promise.then(function() {\n $scope.$close();\n }, function(err) {\n alertService.addApiError(err,'Delete failed');\n });\n };\n}])\n\n.controller('StaffOfflineCirculationCtrl', [\"$scope\", \"kwFileUploadSvc\", \"userService\", function ($scope, kwFileUploadSvc, userService) {\n\n if(userService.can({circulate : 'offline_circ_import'})){\n $scope.canImport = true;\n }\n\n if(userService.can({circulate : 'offline_circ_export'})){\n $scope.canExport = true;\n }\n\n $scope.importOfflineCirculation = function() {\n kwFileUploadSvc.upload({\n title: 'Upload Circulation File',\n description: 'Upload .koc file.',\n instructions: false,\n formdata: {},\n inputs: [],\n url: '/bvcgi/offline_circ/process_koc.pl'\n });\n };\n\n\n}])\n\n\n// Batch Item Mutator\n.controller('StaffBimCtrl', [\"$scope\", \"$state\", \"$uibModal\", \"kwApi\", \"kwFileUploadSvc\", \"userService\", function ($scope, $state, $uibModal, kwApi, kwFileUploadSvc, userService) {\n if ($state.current.name == 'staff.tools.bim') {\n $state.go('staff.tools.bim.index');\n }\n\n\n $scope.userCan = {\n modify: userService.can({editcatalogue: 'batch_item_edit'}),\n delete: userService.can({editcatalogue: 'batch_item_delete'}),\n };\n\n // Common to multiple views\n $scope.getReportSlug = function(i,verbose) {\n if ((i.metadata || {}).report) {\n if (verbose) {\n return ''\n + i.metadata.report.processed + \" Processed, \"\n + i.metadata.report.skipped + \" Skipped, \"\n + i.metadata.report.exception + \" Errors, \"\n + i.metadata.report.redundant + \" Duplicates\";\n }\n else {\n return ''\n + i.metadata.report.processed + \"/\"\n + i.metadata.report.skipped + \"/\"\n + i.metadata.report.exception + \"/\"\n + i.metadata.report.redundant + \"\";\n }\n }\n else {\n if (verbose) {\n return \"New\";\n }\n else {\n return '';\n }\n }\n };\n\n $scope._createFromEntry = function(itemList,keyField) {\n return $uibModal.open({\n backdrop: 'static',\n templateUrl: '/app/static/partials/staff/bim/itemlist-manual-edit-modal.html',\n controller: 'StaffBimManualEditCtrl',\n resolve: {\n entries: function() {\n if (itemList && keyField) {\n return kwApi.ItemList.getEntries({id:itemList.id}).$promise.then(function(entries) {\n return entries.map(function(entry) { return (entry.item||{})[keyField]; });\n });\n }\n else {\n return [];\n }\n },\n itemList: function() {\n return itemList || {name: \"\"};\n },\n fixedKeyField: function() {\n return keyField;\n }\n }\n }).result;\n };\n\n $scope._createFromSearch = function(itemList) {\n return $uibModal.open({\n backdrop: 'static',\n templateUrl: '/app/static/partials/staff/bim/itemlist-attribute-search-modal.html',\n controller: 'StaffBimAttributeSearchCtrl',\n resolve: {\n itemList: function() {\n return itemList || {name: \"\"};\n },\n }\n }).result;\n };\n\n $scope._createFromUpload = function(itemList) {\n var inputs = itemList\n ? [{name: 'id', type: 'hidden', value: itemList.id}]\n : [{name: 'name', type: 'text', label: 'Name', instructions: \"\"}];\n\n inputs.push(\n {name: 'key', type: 'select', label: 'Type', \n instructions: \"Select whether the file contains item numbers or barcodes\",\n values: [\n {value: 'id', display: 'Item Numbers'},\n {value: 'barcode', display: 'Barcodes'},\n ]\n }\n );\n inputs.push(\n {name: 'skipInvalid', type: 'select', label: 'On Invalid', \n instructions: \"What to do when encountering an invalid barcode/item number\",\n values: [\n {value: 1, display: 'Ignore invalid entries'},\n {value: 0, display: 'Fail and return'},\n ]\n }\n );\n return kwFileUploadSvc.upload({\n title: 'Upload Item List',\n description: 'Upload a text list of item numbers or barcodes',\n instructions: false,\n formdata: {\n id: (itemList ? itemList.id : undefined),\n name: (itemList ? itemList.name : ''),\n key: 'barcode',\n skipInvalid: 1,\n },\n inputs: inputs,\n url: '/api/item-list?op=save-from-upload',\n });\n };\n\n $scope._mutateItems = function(itemList, op, ids) {\n return $uibModal.open({\n backdrop: 'static',\n templateUrl: '/app/static/partials/staff/bim/itemlist-mutate-items-modal.html',\n controller: 'StaffBimMutateItemsCtrl',\n size: 'lg',\n resolve: {\n itemList: function() {\n return kwApi.ItemList.get({id: itemList.id}).$promise;\n },\n operation: function() {\n return op;\n },\n selectedItems: function() {\n return ids;\n },\n itemStatuses: function() {\n return kwApi.ItemStatus.query().$promise;\n },\n }\n }).result;\n };\n}])\n\n.controller('StaffBimIndexCtrl', [\"$scope\", \"$interval\", \"itemLists\", \"kwApi\", \"alertService\", \"$state\", \"kohaDlg\", \"branches\", \"userService\", \"uiGridConstants\",\n function ($scope, $interval, itemLists, kwApi, alertService, $state, kohaDlg, branches, userService, uiGridConstants) {\n $scope.itemLists = itemLists\n .map(function(e) {e.report_slug = $scope.getReportSlug(e); return e});\n\n var branchopts = branches\n .map(x => ({value: x.branchcode, label: x.branchname + ' ('+x.branchcode+')'}))\n .sort((a,b) => {return a.label < b.label ? -1 : 1})\n\n $scope.currentPage = 1;\n\n $scope.gridOptions = {\n data: \"itemLists\",\n enableSorting: true,\n enableColumnResizing: true,\n enableColumnReordering: false,\n enableFiltering: true,\n multiSelect: false,\n enableSelectAll: false,\n showGridFooter:true,\n enableFullRowSelection: true,\n\n saveWidths: true,\n saveOrder: false,\n saveScroll: false,\n saveFocus: false,\n saveVisible: false,\n saveSort: true,\n saveFilter: true,\n savePagination: false,\n savePinning: false,\n saveGrouping: false,\n saveGroupingExpandedStates: false,\n saveTreeView: false,\n saveSelection: false,\n\n rowTemplate: '
    ',\n columnDefs: [\n { field: 'name', displayName: 'Name', enableCellEdit: true,\n cellTemplate: ''\n },\n { field: 'created_date', displayName: 'Created on', enableCellEdit: false, type: 'date', cellFilter: 'dateFmt', enableFiltering: false },\n { field: 'branch_code', displayName: 'Branch', enableCellEdit: false, sortCellFiltered: false,\n filter: {\n type: uiGridConstants.filter.SELECT,\n selectOptions: branchopts\n } },\n { field: 'entries_count', displayName: 'Item count', enableCellEdit: false, enableFiltering: false },\n { field: 'status', enableCellEdit: false,\n filter: {\n type: uiGridConstants.filter.SELECT,\n selectOptions: [ {value: 'New', label: 'New'}, {value: 'Processed', label: 'Processed'} ]\n } },\n { field: 'report_slug', displayName: 'OK/Skip/Xcp/Dup', enableCellEdit: false, enableFiltering: false, enableSorting: false },\n ]\n };\n\n $scope.saveRow = function( rowEntity ) {\n var promise = kwApi.ItemList.setName({id: rowEntity.id, name: rowEntity.name},{}).$promise.then(function(rv) {\n alertService.add({msg: \"Item list \"+rowEntity.id+\" now named \\\"\"+rowEntity.name+\"\\\".\"});\n $scope.selectedCount = 0;\n $scope.selectedList = null;\n $scope.reload();\n }, function(err) {\n alertService.addApiError(err, 'Rename failed');\n $scope.reload();\n });\n $scope.gridApi.rowEdit.setSavePromise(rowEntity, promise);\n };\n\n $scope.selectChange = function(e){\n if (e.isSelected) {\n $scope.selectedCount = 1;\n $scope.selectedList = e.entity;\n } else {\n $scope.selectedCount = 0;\n $scope.selectedList = null;\n }\n };\n\n $scope.saveOpts = function() {\n var state = $scope.gridApi.saveState.save();\n userService.setPref(\"bim_index_grid_opts\", state);\n };\n\n $scope.restoreOpts = function() {\n $interval(function() {\n var state = userService.getPref(\"bim_index_grid_opts\");\n if (! state) {\n return;\n }\n $scope.gridApi.saveState.restore( $scope, state );\n }, 50, 1);\n };\n\n $scope.gridOptions.onRegisterApi = function(gridApi){\n $scope.gridApi = gridApi;\n gridApi.rowEdit.on.saveRow($scope, $scope.saveRow);\n gridApi.selection.on.rowSelectionChanged($scope, $scope.selectChange);\n $scope.restoreOpts();\n };\n\n $scope.selectedCount = 0;\n\n $scope.rebuildIndex = function() {\n $scope.itemListIndex = {};\n $scope.itemLists.forEach(function(e) {\n $scope.itemListIndex[e.id] = e;\n });\n };\n $scope.rebuildIndex();\n\n $scope.reload = function() {\n $scope.gridApi.core.notifyDataChange( uiGridConstants.dataChange.ALL);\n kwApi.ItemList.query({view: 'details', start: 0, sort: null, reverse: null}).$promise.then(function(rv) {\n $scope.itemLists = rv\n .map(function(e) {e.report_slug = $scope.getReportSlug(e); return e});\n $scope.rebuildIndex();\n });\n };\n\n $scope.$on('sys.signal.BatchDeleteItems', function(evnt, args) { \n $scope.reload();\n });\n $scope.$on('sys.signal.BatchModifyItems', function(evnt, args) { \n $scope.reload();\n });\n\n $scope.delete = function(itemList) {\n kohaDlg.dialog({\n heading: 'Are you sure?',\n message: 'Are you sure you want to delete this item list? This CANNOT be undone.',\n buttons: [{val: true, label: 'Yes', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n }).result.then(function(rv) {\n if (rv) {\n kwApi.ItemList.delete({id: itemList.id}).$promise.then(function(rv) {\n alertService.add({msg: \"Item list deleted\"});\n $scope.selectedCount = 0;\n $scope.selectedList = null;\n $scope.reload();\n }, function(err) {\n alertService.addApiError(err, 'Delete failed');\n $scope.reload();\n });\n }\n });\n };\n\n $scope.createFromEntry = function(itemList,keyField) {\n $scope._createFromEntry(itemList,keyField).then(function() {\n $scope.reload();\n });\n };\n\n $scope.createFromSearch = function(itemList) {\n $scope._createFromSearch(itemList).then(function() {\n $scope.reload();\n });\n };\n\n $scope.createFromUpload = function(itemList) {\n $scope._createFromUpload(itemList).then(function() {\n $scope.reload();\n });\n };\n\n $scope.mutateItems = function(itemList,op) {\n $scope._mutateItems(itemList,op).then(function(rv) {\n $scope.reload();\n });\n };\n}])\n\n.controller('StaffBimDetailsCtrl', [\"$scope\", \"itemList\", \"itemListEntries\", \"kwApi\", \"alertService\", \"kohaDlg\", \"loading\", function ($scope, itemList, itemListEntries, kwApi, alertService, kohaDlg, loading) {\n $scope.itemList = itemList;\n $scope.itemListEntries = itemListEntries;\n $scope.totalRows = ($scope.itemListEntries[0]||{})._embed_total_rows||0;\n $scope.selected = {};\n $scope.selectedCount = 0;\n\n $scope.bimItemFieldsTmpl = '/app/static/partials/components/bim-item-fields.html';\n for (var i = 0; i b.label ? 1 : 0) });\n $scope.itemList = itemList;\n $scope.op = 'equals';\n $scope.item = {}; // Dummy item for koha-item-data-input\n $scope.field = {};\n\n $scope.save = function() {\n console.dir($scope);\n $scope.itemList._search = {field: $scope.field.code, op: $scope.op};\n if ($scope.allowArbitrary)\n $scope.itemList._search.value = $scope.value;\n else if ($scope.field.custom)\n $scope.itemList._search.value = $scope.item.fields[$scope.field.code];\n else\n $scope.itemList._search.value = $scope.item[$scope.field.code];\n\n kwApi.ItemList.saveFromSearch({},$scope.itemList).$promise.then(function(rv) {\n $scope.$close();\n }, function(err) {\n alertService.addApiError(err, 'Search failed');\n });\n };\n}])\n\n\n.controller('StaffBimMutateItemsCtrl', [\"$scope\", \"operation\", \"selectedItems\", \"itemList\", \"kwApi\", \"configService\", \"alertService\", \"itemStatuses\", function ($scope, operation, selectedItems, itemList, kwApi, configService, alertService, itemStatuses) {\n $scope.itemFields = angular.copy(configService.ItemFields.sort(function(a,b) { return (a.label < b.label ? -1 : a.label > b.label ? 1 : 0) })).filter(function(x) {\n if (x.code === 'biblionumber') return false;\n if (x.code === 'biblioitemnumber') return false;\n if (x.code === 'itemnumber') return false;\n if (x.code === 'datelastseen') return false;\n if (x.code === 'barcode') return false;\n if (x.code === 'uuid') return false;\n if (x.code === '_availability') return false;\n if (x.code === 'cn_sort') return false;\n return true;\n });\n $scope.itemFields.unshift({code: '_itemstatus', label: 'Custom Item Status'});\n\n $scope.itemStatuses = itemStatuses;\n\n $scope.itemList = itemList;\n $scope.item = {};\n $scope.async = 1;\n $scope.modItemFields = [];\n $scope.constraints = {};\n $scope.operation = operation;\n if (selectedItems && selectedItems.length) {\n $scope.itemCount = '' + selectedItems.length + ' Selected';\n $scope.selectedItems = selectedItems;\n }\n else {\n $scope.itemCount = 'All';\n }\n\n var itemFieldIndex = {};\n $scope.itemFields.forEach(function(r) {\n itemFieldIndex[r.code] = r;\n });\n\n\n if (itemList.metadata) {\n var settings = itemList.metadata[$scope.operation] || {};\n\n $scope.constraints = settings.constraints || {};\n $scope.delete_title = settings.delete_title || '';\n $scope.item = {};\n $scope.modItemFields = settings.mod_item || [];\n $scope.modItemFields.forEach(function(f) {\n if (f._action == 'set' || f._action == 'default') {\n $scope.item[f.code] = f.value;\n }\n });\n }\n\n $scope.itemFieldAdd = function(newField) {\n if (newField) {\n $scope.itemFields.forEach(function(f) {\n if (f.code == newField) {\n $scope.modItemFields.push(angular.extend({}, f, {\n custom: false, \n _action: (f.code == '_itemstatus' ? 'addstatus' : 'set'),\n _itemstatus: (f.code == '_itemstatus'),\n }));\n $scope.item[f.code] = '';\n }\n });\n $scope.newItemField = undefined;\n }\n };\n\n $scope.$watch('newItemField', function(newVal) {\n if (newVal) {\n $scope.itemFieldAdd($scope.newItemField);\n }\n });\n\n $scope.itemFieldDel = function(idx) {\n delete $scope.item[$scope.modItemFields[idx].code]; \n $scope.modItemFields.splice(idx,1);\n };\n\n $scope.process = function() {\n $scope.modItemFields.forEach(function(mf) {\n if (mf._action == 'set' || mf._action == 'default') { \n mf.value = $scope.item[mf.code];\n }\n });\n\n var method = (operation == 'delete' ? 'deleteItems' : 'modifyItems');\n kwApi.ItemList[method]({\n id: itemList.id,\n async: $scope.async,\n },{\n mod_item: $scope.modItemFields,\n constraints: $scope.constraints,\n delete_title: $scope.delete_title,\n selected_items: $scope.selectedItems,\n }).$promise.then(function() {\n alertService.add({msg: ($scope.async ? \"Process started, you will be notified when it is finished\" : \"Process completed\")});\n $scope.$close();\n }, function(err) {\n alertService.addApiError(err, ($scope.async ? \"Unable to start process\" : \"Unable to process items\"));\n });\n };\n\n $scope.save = function() {\n $scope.modItemFields.forEach(function(mf) {\n if (mf._action == 'set' || mf._action == 'default') { \n mf.value = $scope.item[mf.code];\n }\n });\n kwApi.ItemList.saveMetadata({\n id: itemList.id,\n key: $scope.operation,\n },{\n mod_item: $scope.modItemFields,\n constraints: $scope.constraints,\n delete_title: $scope.delete_title,\n }).$promise.then(function() {\n alertService.add({msg: \"Settings saved\"});\n }, function(err) {\n alertService.addApiError(err, 'Unable to save');\n });\n };\n}])\n\n\n// Message Template Editor\n.controller('StaffMteCtrl', [\"$scope\", \"$state\", \"userService\", function ($scope, $state, userService) {\n if ($state.current.name == 'staff.tools.mte') {\n $state.go('staff.tools.mte.index');\n }\n\n $scope.userCan = {\n templates: userService.can({tools: {edit_notices: 'update'}}),\n branches: userService.can({tools: {edit_notices: 'branch_assign'}}, '*'),\n };\n\n}])\n\n.controller('StaffMteIndexCtrl', [\"$scope\", \"$state\", \"$stateParams\", \"templates\", \"branches\", \"userService\", \"$uibModal\", \"alertService\", \"kwApi\", \"configService\", \"loading\", \"kohaDlg\", function ($scope, $state, $stateParams, templates, branches, userService, $uibModal, alertService, kwApi, configService, loading, kohaDlg) {\n var filteredBranches = $scope.branches = branches.filter(function(branch) {\n return userService.can({tools: {edit_notices: 'branch_assign'}}, \"branch=\" + branch.branchcode) ||\n (branch.branchcode == userService.login_branch && userService.can({tools: {edit_notices: 'branch_assign'}}, 'branch=my_own_branch'));\n }).sort(function(a,b) {\n return (a.branchname < b.branchname ? -1 : a.branchname > b.branchname ? 1 : 0)\n });\n\n\n var hasBranch = {};\n filteredBranches.forEach(function(b) {\n hasBranch[b.branchcode] = true;\n });\n\n $scope.mode = $stateParams.mode || ($scope.userCan.templates ? 'templates' : 'branch');\n $scope.branch = $stateParams.branch || userService.login_branch;\n $scope.order = {};\n\n $scope.endpointTypes = [\n {code: 'popup', name: 'Popup'},\n {code: 'list', name: 'List'},\n {code: 'rss', name: 'RSS'},\n {code: 'email', name: 'Email'},\n ];\n\n if (configService.TalkingTechText) {\n $scope.endpointTypes.push({code: 'tttxt', name: 'TTtext'});\n $scope.TalkingTech = true;\n }\n\n if (configService.TalkingTechVoice) {\n $scope.endpointTypes.push({code: 'ttvox', name: 'TTvoice'});\n $scope.TalkingTech = true;\n }\n\n $scope.collapsed = {};\n $scope.flat = false;\n\n $scope.setMode = function(mode) {\n if (mode == 'own-branch') {\n $state.go('staff.tools.mte.index',{mode:'branch', branch:userService.login_branch});\n }\n else if (mode == 'any-branch') {\n $uibModal.open({\n backdrop: true,\n size: 'sm',\n templateUrl: '/app/static/partials/staff/tools/mte/branch-select-modal.html',\n controller: [\"$scope\", function($scope) {\n $scope.branches = filteredBranches;\n $scope.$watch('sel', function(newVal) {\n if (newVal) {\n $scope.$close(newVal);\n }\n });\n }],\n }).result.then(function(v) {\n if (v) {\n $state.go('staff.tools.mte.index',{mode:'branch', branch:v});\n }\n });\n }\n else {\n $state.go('staff.tools.mte.index',{mode:mode});\n }\n };\n\n $scope.collapseAll = function() {\n $scope.flat = false;\n $scope.displayTemplates = $scope.templates;\n $scope.templates.forEach(function(tmpl) {\n $scope.collapsed[tmpl.category] = true;\n });\n };\n\n $scope.expandAll = function() {\n $scope.flat = false;\n $scope.displayTemplates = $scope.templates;\n $scope.templates.forEach(function(tmpl) {\n $scope.collapsed[tmpl.category] = false;\n });\n };\n\n $scope.viewFlat = function() {\n $scope.flat = true;\n $scope.displayTemplates = $scope.uncategorizedTemplates;\n };\n\n\n\n $scope.totalCols = 7 + $scope.endpointTypes.length;\n\n $scope.processTemplates = function(tmpl) {\n $scope.branchTemplates = {};\n $scope.availableTemplates = {};\n $scope.typeIndex = {};\n $scope.codeIndex = {};\n $scope.allTemplates = [];\n\n var allFlatTemplates = [];\n var semiFlat = [];\n var totalCategories = 0;\n tmpl.forEach(function(c) {\n if (!userService.can({tools: {edit_notices: 'access'}}, \"msgcat=\" + c.category)) return;\n totalCategories++;\n\n var flatTemplates = [];\n c.types.forEach(function(mt) {\n mt.id = mt.type.id;\n var branchTemplates = {};\n var availableTemplates = [];\n var templateCodeBranches = {};\n\n mt.branch_message_templates.forEach(function(bmt) {\n branchTemplates[bmt.branch_code] = bmt.template_code;\n if (!templateCodeBranches[bmt.template_code])\n templateCodeBranches[bmt.template_code] = [];\n templateCodeBranches[bmt.template_code].push(bmt.branch_code);\n });\n\n var msgType = mt.type;\n $scope.typeIndex[mt.id] = mt;\n mt.template_codes.forEach(function(templateSet) {\n var canEdit = $scope.userCan.templates || (templateSet.owning_branch && hasBranch[templateSet.owning_branch]);\n if (templateSet.owning_branch && !canEdit)\n return;\n\n var branchesList = (templateCodeBranches[templateSet.code] || [])\n .filter(function(b) { return hasBranch[b] })\n .join(', ');\n\n $scope.allTemplates.push(templateSet);\n availableTemplates.push({code: templateSet.code, name: templateSet.code});\n var row = angular.extend({}, mt.type, {\n templateCode: templateSet.code,\n templateSet: templateSet.templates,\n branches: branchesList,\n canEdit: canEdit,\n owningBranch: templateSet.owning_branch,\n //branchTemplates: branchTemplates,\n //availableTemplates: availableTemplates,\n isPrimary: (!templateSet.code || (mt.type.code == templateSet.code)),\n });\n flatTemplates.push(row);\n allFlatTemplates.push(row);\n $scope.codeIndex[templateSet.code] = row;\n });\n\n $scope.branchTemplates[mt.type.id] = branchTemplates || {};\n if ($scope.userCan.branches) {\n availableTemplates.push({code: '', name: 'New...'});\n }\n $scope.availableTemplates[mt.type.id] = availableTemplates;\n });\n flatTemplates = flatTemplates.sort(function(a,b) {\n if (a.code < b.code) return -1;\n if (a.code > b.code) return 1;\n if (a.isPrimary && !b.isPrimary) return -1;\n if (!a.isPrimary && b.isPrimary) return 1;\n if (a.templateCode < b.templateCode) return -1;\n if (a.templateCode > b.templateCode) return 1;\n });\n semiFlat.push({category: c.category, templates: flatTemplates});\n if (!$scope.order[c.category]) {\n $scope.order[c.category] = {};\n }\n });\n allFlatTemplates = allFlatTemplates.sort(function(a,b) {\n if (a.category < b.category) return -1;\n if (a.category > b.category) return 1;\n if (a.code < b.code) return -1;\n if (a.code > b.code) return 1;\n if (a.isPrimary && !b.isPrimary) return -1;\n if (!a.isPrimary && b.isPrimary) return 1;\n if (a.templateCode < b.templateCode) return -1;\n if (a.templateCode > b.templateCode) return 1;\n });\n\n $scope.templates = semiFlat;\n $scope.uncategorizedTemplates = [{category: 'All Templates', templates: allFlatTemplates}];\n if (!$scope.order['All Templates']) {\n $scope.order['All Templates'] = {};\n }\n $scope.displayTemplates = ($scope.flat ? $scope.uncategorizedTemplates : $scope.templates);\n if (totalCategories > 1) {\n $scope.userCan.multicat = true;\n }\n };\n\n $scope.processTemplates(templates);\n\n $scope.$watch('branchTemplates', function(newVal, oldVal) {\n if (!newVal && oldVal) return;\n angular.forEach(newVal, function(branches, typeId) {\n angular.forEach(branches, function(templateCode, branchCode) {\n if (oldVal[typeId][branchCode] != newVal[typeId][branchCode]) {\n if (newVal[typeId][branchCode] === '') {\n $scope.addTemplate($scope.typeIndex[typeId].type,branchCode);\n }\n else {\n loading.add();\n kwApi.MessageType.setBranchTemplate({id: typeId},{branch_code: branchCode, template_code: newVal[typeId][branchCode]}).$promise.then(function(rv) {\n loading.resolve();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err, 'Unable to save');\n $state.go('staff.tools.mte.index', {mode:$scope.mode, branch: $scope.branch}, {reload: true});\n });\n }\n }\n });\n });\n }, true);\n\n $scope.currentPage = 1;\n\n $scope.addTemplate = function(mt, applyToBranch) {\n var cloned = angular.copy(mt);\n delete cloned['templateSet'];\n $scope.editTemplate(cloned, applyToBranch).then(function(newTemplateSet) {\n var templateCode;\n angular.forEach(newTemplateSet, function(val, key) {\n if (val.template_code)\n templateCode = val.template_code;\n });\n\n if (applyToBranch && templateCode) {\n loading.add();\n kwApi.MessageType.setBranchTemplate({id: mt.id},{branch_code: applyToBranch, template_code: templateCode}).$promise.then(function(rv) {\n return kwApi.MessageTemplate.tree().$promise;\n }).then(function(rv) {\n $scope.processTemplates(rv);\n loading.resolve();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err, 'Unable to save');\n $state.go('staff.tools.mte.index', {mode:$scope.mode, branch: $scope.branch}, {reload: true});\n });\n }\n else {\n $state.go('staff.tools.mte.index', {mode:$scope.mode, branch: $scope.branch}, {reload: true});\n }\n });\n };\n\n $scope.editTemplate = function(mt, applyToBranch) {\n return $uibModal.open({\n backdrop: 'static',\n size: 'lg',\n templateUrl: '/app/static/partials/staff/tools/mte/edit-template-modal.html',\n controller: 'StaffMteEditTemplateCtrl',\n resolve: {\n allTemplates: function() {\n return $scope.allTemplates;\n },\n branch: function() {\n return applyToBranch;\n },\n branches: function() {\n return $scope.branches;\n },\n messageType: function() {\n return mt;\n },\n templates: function() {\n return mt.templateSet;\n },\n endpointTypes: function() {\n return $scope.endpointTypes;\n },\n typeDef: function() {\n return kwApi.MessageType.getWithParameters({id: mt.id}).$promise;\n },\n templateCodes: function() {\n return kwApi.MessageTemplate.getTemplateCodes().$promise;\n },\n\n },\n }).result;\n };\n\n $scope.deleteTemplate = function(mt) {\n kohaDlg.dialog({\n heading: 'Are you sure?',\n message: 'Are you sure you want to delete these templates? This CANNOT be undone.',\n buttons: [{val: true, label: 'Yes', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n }).result.then(function(rv) {\n if (!rv) return;\n\n var ids = [];\n angular.forEach(mt.templateSet, function(val, key) {\n if (val) {\n ids.push(val.id);\n }\n });\n\n loading.add();\n kwApi.MessageTemplate.forAll({action: 'delete', ids: ids}).$promise.then(function(rv) {\n return kwApi.MessageTemplate.tree().$promise;\n }).then(function(rv) {\n $scope.processTemplates(rv);\n loading.resolve();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err, 'Delete failed');\n });\n });\n };\n}])\n\n.controller('StaffMteEditTemplateCtrl', [\"$scope\", \"branch\", \"branches\", \"messageType\", \"templates\", \"endpointTypes\", \"allTemplates\", \"templateCodes\", \"typeDef\", \"alertService\", \"kwApi\", \"loading\", \"userService\", function ($scope, branch, branches, messageType, templates, endpointTypes, allTemplates, templateCodes, typeDef, alertService, kwApi, loading, userService) {\n $scope.applyToBranch = branch;\n $scope.branches = branches.sort(function(a,b) {\n return (a.branchname < b.branchname ? -1 : a.branchname > b.branchname ? 1 : 0)\n });\n $scope.allTemplates = allTemplates;\n $scope.parameters = typeDef.parameters;\n $scope.endpointTypes = endpointTypes;\n $scope.messageType = messageType;\n if (templates) {\n $scope.isNew = false;\n $scope.template_code = '';\n angular.forEach(templates, function(val,key) {\n if (val.template_code)\n $scope.template_code = val.template_code;\n });\n $scope.owning_branch = messageType.owningBranch;\n }\n else {\n $scope.isNew = true;\n $scope.template_code = (branch ? (messageType.code + '.' + branch) : '');\n $scope.owning_branch = branch || userService.login_branch;\n }\n $scope.template = templates || {};\n $scope.d = {type: 'popup'};\n\n $scope.existingTemplateCode = {};\n templateCodes.forEach(function(t) {\n $scope.existingTemplateCode[t] = true;\n });\n\n $scope.endpointTypes.forEach(function(type) {\n if (!$scope.template[type.code]) $scope.template[type.code] = {};\n if (!$scope.template[type.code].content) $scope.template[type.code].content = '';\n if (!$scope.template[type.code].name) $scope.template[type.code].name = '';\n });\n\n $scope.validType = {};\n\n $scope.typeClass = function(type) {\n if (!$scope.template[type]) {\n $scope.validType[type] = true;\n return 'btn-outline-secondary';\n }\n else if ($scope.template[type].name && $scope.template[type].content) {\n $scope.validType[type] = true;\n return 'btn-outline-primary';\n }\n else if ($scope.template[type].name || $scope.template[type].content) {\n $scope.validType[type] = false;\n return 'btn-outline-danger';\n }\n else {\n $scope.validType[type] = true;\n return 'btn-outline-secondary';\n }\n };\n\n $scope.formValid = function() {\n if (!$scope.template_code) return false;\n if ($scope.isNew && $scope.existingTemplateCode[$scope.template_code]) return false;\n\n var rv = true;\n angular.forEach($scope.validType, function(val) {\n if (val === false) {\n rv = false;\n }\n });\n return rv;\n };\n\n $scope.clear = function(type) {\n $scope.template[type].name = '';\n $scope.template[type].title = '';\n $scope.template[type].content = '';\n };\n\n $scope.copyFrom = function(p) {\n if (!$scope.template[p])\n return;\n if (!$scope.template[$scope.d.type])\n $scope.template[$scope.d.type] = {};\n $scope.template[$scope.d.type].name = $scope.template[p].name;\n $scope.template[$scope.d.type].title = $scope.template[p].title;\n $scope.template[$scope.d.type].content = $scope.template[p].content;\n };\n\n $scope.copyFromTemplate = function(p) {\n $scope.template = p.templates;\n };\n\n $scope.addParameter = function() {\n\n if (!$scope.parameter.length) return;\n var selections = $scope.parameter.map(function(p) { return '[% ' + p + ' %]'; });\n\n var form = document.form;\n if (!form) {\n $scope.template[$scope.d.type].content += selections.join(', ');\n }\n else {\n var content = form.content;\n if (content.selectionStart || content.selectionStart == \"0\") {\n $scope.template[$scope.d.type].content = content.value.substring(0,content.selectionStart)\n + selections.join(', ')\n + content.value.substring(content.selectionEnd, content.value.length);\n }\n else {\n $scope.template[$scope.d.type].content += selections.join(', ');\n }\n }\n };\n\n $scope.save = function() {\n Object.keys($scope.template).forEach(function(key) {\n if (!$scope.template[key].name) {\n delete $scope.template[key];\n }\n else {\n $scope.template[key].message_endpoint_type = key;\n $scope.template[key].template_code = $scope.template_code;\n $scope.template[key].owning_branch = $scope.owning_branch;\n }\n });\n\n loading.add();\n kwApi.MessageType.replaceTemplateSet({id: $scope.messageType.id}, $scope.template).$promise.then(function(rv) {\n loading.resolve();\n $scope.$close($scope.template);\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err, 'Unable to save');\n });\n };\n\n}])\n\n\n.controller('StaffCronCtrl', [\"$scope\", \"$state\", \"cronScripts\", function ($scope, $state, cronScripts) {\n $scope.cronScripts = cronScripts;\n if ($state.current.name == 'staff.admin.cron') {\n $state.go('staff.admin.cron.index');\n }\n\n\n}])\n\n.controller('StaffCronIndexCtrl', [\"$scope\", \"cronJobs\", \"alertService\", \"kwApi\", \"loading\", \"kohaDlg\", function ($scope, cronJobs, alertService, kwApi, loading, kohaDlg) {\n $scope.cronJobs = cronJobs;\n\n $scope.reload = function() {\n loading.add();\n kwApi.CronJob.query().$promise.then(function(rv) {\n loading.resolve();\n $scope.cronJobs = rv;\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Can\\'t reload');\n });\n };\n\n $scope.delete = function(n) {\n kohaDlg.dialog({\n heading: 'Are you sure?',\n message: 'Are you sure you want to delete this cron script entry? This CANNOT be undone.',\n buttons: [{val: true, label: 'Yes', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n }).result.then(function(rv) {\n if (!rv) return;\n kwApi.CronJob.delete({id: n}).$promise.then(function(rv) {\n $scope.reload();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Can\\'t delete');\n });\n });\n };\n\n $scope.order = {};\n\n}])\n\n.controller('StaffCronDetailsCtrl', [\"$scope\", \"$state\", \"$stateParams\", \"cronJob\", \"cronJobRuns\", \"alertService\", \"kwApi\", \"loading\", function ($scope, $state, $stateParams, cronJob, cronJobRuns, alertService, kwApi, loading) {\n $scope.cronJob = cronJob;\n $scope.runs = cronJobRuns;\n $scope.order = {field: 'created_time', reverse: true};\n $scope.isActive = (cronJob.schedule ? true : false);\n $scope.saveStdout = (cronJob.save_stdout>0 ? true : false);\n $scope.saveStderr = (cronJob.save_stderr>0 ? true : false);\n $scope.saveStderrLevel = cronJob.save_stderr_level || 'warning';\n $scope.notifyStderrLevel = cronJob.notify_stderr_level || 'off';\n $scope.maxRuntime = cronJob.max_runtime || 0;\n if ($scope.cronJob.purge_after === null)\n $scope.cronJob.purge_after = '';\n\n if (!$scope.cronJob.id) {\n $scope.$watch('cronJob.cron_script_id', function(newVal) {\n if (!newVal) return;\n $scope.cronScripts.forEach(function(s) {\n if (s.id != newVal) return;\n $scope.cronJob.args = s.default_args;\n $scope.cronJob.schedule = s.default_schedule;\n $scope.cronJob.description = s.description;\n });\n });\n }\n\n $scope.$watch('isActive', function(newVal) {\n if (newVal && $scope.cronJob.schedule === null)\n if ($scope.cronJob.old_schedule)\n $scope.cronJob.schedule = $scope.cronJob.old_schedule;\n else\n $scope.cronJob.schedule = '0:0:0:1*0:0:0';\n });\n\n\n $scope.reload = function() {\n loading.add();\n kwApi.CronJob.get({id: $scope.cronJob.id}).$promise.then(function(rv) {\n $scope.cronJob = rv;\n $scope.isActive = ($scope.cronJob.schedule ? true : false);\n $scope.saveStdout = ($scope.cronJob.save_stdout>0 ? true : false);\n $scope.saveStderr = ($scope.cronJob.save_stderr>0 ? true : false);\n $scope.saveStderrLevel = $scope.cronJob.save_stderr_level || 'warning';\n $scope.notifyStderrLevel = $scope.cronJob.notify_stderr_level || 'off';\n $scope.maxRuntime = $scope.cronJob.max_runtime || 0;\n if ($scope.cronJob.purge_after === null)\n $scope.cronJob.purge_after = '';\n loading.resolve();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Can\\'t reload');\n });\n\n loading.add();\n kwApi.CronJob.getRuns({id: $stateParams.id}).$promise.then(function(rv) {\n loading.resolve();\n $scope.runs = rv;\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Can\\'t reload');\n });\n }\n\n $scope.save = function() {\n if (!$scope.isActive) {\n if ($scope.cronJob.schedule)\n $scope.cronJob.old_schedule = $scope.cronJob.schedule;\n $scope.cronJob.schedule = null;\n }\n $scope.cronJob.save_stdout = ($scope.saveStdout ? 1 : 0);\n $scope.cronJob.save_stderr = ($scope.saveStderr ? 1 : 0);\n $scope.cronJob.save_stderr_level = $scope.saveStderrLevel;\n $scope.cronJob.notify_stderr_level = $scope.notifyStderrLevel;\n $scope.cronJob.max_runtime = $scope.maxRuntime;\n if (!($scope.cronJob.purge_after === '0' || (1*$scope.cronJob.purge_after) > 0))\n $scope.cronJob.purge_after = null;\n\n loading.add();\n if ($scope.cronJob.id) {\n kwApi.CronJob.put({id: $scope.cronJob.id}, $scope.cronJob).$promise.then(function() {\n loading.resolve();\n $scope.reload();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Can\\'t save');\n });\n }\n else {\n kwApi.CronJob.save($scope.cronJob).$promise.then(function(rv) {\n loading.resolve();\n $state.go('staff.admin.cron.view', {id: rv.id});\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Can\\'t save');\n });\n }\n };\n\n\n $scope.run = function() {\n loading.add();\n kwApi.CronJob.put({id: $scope.cronJob.id}, $scope.cronJob).$promise.then(function() {\n return kwApi.CronJob.execute({id: $scope.cronJob.id},{}).$promise;\n }).then(function(rv) {\n loading.resolve();\n alertService.add({msg: \"Job started; you will be notified when complete\", type: \"info\"});\n $scope.reload();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Can\\'t run');\n });\n };\n\n $scope.remove_run = function(n) {\n loading.add();\n kwApi.CronJob.deleteRun({id: $scope.cronJob.id, rid: n.id}).$promise.then(function() {\n loading.resolve();\n $scope.reload();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Remove failed');\n });\n };\n\n $scope.isValid = function() {\n $scope.err = '';\n if (!$scope.cronJob.cron_script_id) {\n $scope.err = 'Script is required';\n return false;\n }\n if ($scope.isActive && !$scope.cronJob.schedule) {\n $scope.err = 'Schedule is required';\n return false;\n }\n\n if ($scope.cronJob.args.filter(function(s) { return s === '' }).length != 0) {\n $scope.err = 'All arguments must have values';\n return false;\n }\n\n return true;\n };\n\n $scope.scriptSelectConfig = {\n maxItems: 1,\n valueField: 'id',\n labelField: 'script',\n searchField: ['script','description'],\n create: false,\n render: {\n item: function(rec, escape) {\n if (!rec) return '';\n return '
    ' + escape(rec.script) + ' - ' + escape(rec.description) + '
    ';\n },\n option: function(rec, escape) {\n if (!rec) return '';\n return '
    ' + escape(rec.script) + ' - ' + escape(rec.description) + '
    ';\n },\n },\n };\n\n $scope.canRemove = function(n) {\n if (n.status == 'started')\n return false;\n else if (n.cron_spec && n.status == 'pending')\n return false;\n else\n return true;\n };\n\n\n $scope.$on('sys.signal.CronJob', function(evnt, args) { \n if (args.cron_job_id == cronJob.id) {\n loading.add();\n kwApi.CronJob.getRuns({id: $stateParams.id}).$promise.then(function(rv) {\n loading.resolve();\n $scope.runs = rv;\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Can\\'t reload');\n });\n }\n });\n\n}])\n\n.controller('StaffCronRunDetailsCtrl', [\"$scope\", \"cronJob\", \"run\", function ($scope, cronJob, run) {\n $scope.cronJob = cronJob;\n $scope.run = run;\n $scope.isActive = (cronJob.schedule ? true : false);\n $scope.saveStdout = (cronJob.save_stdout>0 ? true : false);\n $scope.saveStderr = (cronJob.save_stderr>0 ? true : false);\n\n}])\n\n.controller('StaffTraceCtrl', [\"$scope\", \"traceOptions\", \"$http\", \"$rootScope\", function($scope, traceOptions, $http, $rootScope) {\n $scope.traceOptions = traceOptions;\n $scope.enabledTraceOptions = {};\n\n $scope.readHeader = function() {\n };\n\n $scope.setOptions = function(opts) {\n $scope.selectedOptions = '';\n $scope.enabledTraceOptions = {};\n $scope.enabledTraceOptionsList = [];\n if (opts && opts.length) {\n opts.forEach(function(o) {\n $scope.enabledTraceOptions[o] = true;\n });\n\n $scope.traceOptions.forEach(function(group) {\n group.items.forEach(function(item) {\n if ($scope.enabledTraceOptions[item.value]) {\n $scope.enabledTraceOptionsList.push(item.value);\n }\n });\n });\n $scope.selectedOptions = $scope.enabledTraceOptionsList.join(', ');\n }\n };\n\n $scope.processTrace = function(t) {\n t.patron = (t.last_name || '?') + ', ' + (t.first_name || '?') + ' (' + t.userid + ')';\n if (t.headers) {\n t.headers = t.headers.split(/[\\n\\r]+/);\n }\n if (t.data && t.data.exception_reason) {\n t.title = t.data.exception_reason;\n }\n else if (t.data && t.data.stack_brief && t.data.stack_brief[0]) {\n t.title = t.data.stack_brief[0].func;\n }\n else {\n t.title = '';\n }\n return t;\n };\n\n var opts = $http.defaults.headers.common['X-KohaOX-Trace'];\n if (opts) {\n $scope.setOptions(opts.split(/,\\s*/));\n }\n else {\n $scope.setOptions(false);\n }\n\n $scope.$on('$destroy', $rootScope.$on('setApiTracing', function(evnt, opts) {\n $scope.setOptions(opts);\n }));\n}])\n\n.controller('StaffTraceIndexCtrl', [\"$scope\", \"$uibModal\", \"traces\", \"kwApi\", \"userService\", \"$http\", \"$rootScope\", \"$timeout\", \"alertService\", function($scope, $uibModal, traces, kwApi, userService, $http, $rootScope, $timeout, alertService) {\n $scope.order = {field: 'stamp', reverse: true};\n\n $scope.pager = new KOHA.Pager({numResults: traces.length, offset: 0, numPerPage: 20});\n $scope.page = {start: 0, count: 20};\n\n $scope.displayTraces = [];\n\n $scope.refresh = function() {\n // filter by tags\n var displayList = $scope.traces;\n $scope.totalFiltered = displayList.length;\n \n displayList.sort(function(a,b) {\n var ordf = $scope.order.field;\n var n = ($scope.order.reverse ? -1 : 1);\n if (typeof a[ordf] !== 'string') {\n return -n;\n } else if (typeof b[ordf] !== 'string') {\n return n;\n }\n if (a[ordf].toUpperCase() < b[ordf].toUpperCase()) {\n return -n;\n }\n else if (a[ordf].toUpperCase() > b[ordf].toUpperCase()) {\n return n;\n }\n else {\n return 0;\n }\n });\n displayList = displayList.slice($scope.page.start, $scope.page.start + (1*$scope.page.count));\n $scope.displayTraces.replaceWith(displayList);\n };\n\n $scope.toPage = function(page) {\n $scope.page.start = (page-1) * $scope.page.count;\n $scope.refresh();\n };\n\n\n $scope.sortChanged = function() {\n $scope.page.start = 0;\n $scope.pager.page = 1;\n $scope.refresh();\n };\n\n $scope.$watch('order', function(newVal) {\n if (!newVal) return;\n $scope.sortChanged();\n }, true);\n\n\n $scope.processTraces = function(tt) {\n $scope.traces = [];\n tt.forEach(function(t) {\n $scope.traces.push($scope.processTrace(t));\n });\n $scope.pager.numResults = $scope.traces.length;\n };\n $scope.processTraces(traces);\n\n $scope.canAllMembers = userService.can({tools: {trace: 'other'}});\n $scope.canView = userService.can({tools: {trace: 'other'}}) || userService.can({tools: {trace: 'self'}});\n $scope.canCreate = userService.can({tools: {trace: 'create'}});\n\n $scope.reload = function() {\n var promise = $scope.allMembers\n ? kwApi.SessionApiMetadata.query().$promise\n : kwApi.SessionApiMetadata.query({member_id: userService.id}).$promise;\n promise.then(function(rv) {\n $scope.processTraces(rv);\n $scope.refresh();\n });\n };\n\n $scope.setAllMembers = function(n) {\n $scope.allMembers = n ? true : false;\n $scope.reload();\n };\n\n $scope.genError = function(type,code) {\n if (type.substr(0,8) == 'Frontend') {\n var prevOpts = $rootScope.tracingOptions;\n $rootScope.setApiTracing(['FrontendLog','FrontendWarn','FrontendError']);\n if (type == 'FrontendLog') {\n console.log('This is a test log');\n }\n else if (type == 'FrontendWarn') {\n console.warn('This is a test warning');\n }\n else if (type == 'FrontendError') {\n console.error('This is a test error');\n }\n $rootScope.setApiTracing(prevOpts);\n $timeout(function() { $scope.reload() }, 0);\n }\n else {\n $http.post('/api/session-api-metadata?op=throw&type='+type+'&code='+code, {}, {headers:{'X-KohaOX-Trace':'all'}}).then(function() {\n $scope.reload();\n }, function() {\n $scope.reload();\n });\n }\n };\n \n $scope.toggleOptions = function() {\n if ($scope.enabledTraceOptionsList.length > 0) {\n $rootScope.setApiTracing(false);\n }\n else {\n $uibModal.open({\n backdrop: 'static',\n templateUrl: '/app/static/partials/staff/tools/trace/edit-options-modal.html',\n controller: 'StaffTraceEditOptionsCtrl',\n resolve: {\n enabledTraceOptions: function() {\n return $scope.enabledTraceOptions;\n },\n traceOptions: function() {\n return $scope.traceOptions;\n },\n forUser: function() {\n return false;\n },\n },\n }).result.then(function(s) {\n if (s) {\n var opts = [];\n angular.forEach(s.options, function(val, key) {\n if (val) opts.push(key);\n });\n $rootScope.setApiTracing(opts,s.duration);\n }\n });\n }\n };\n\n $scope.traceUser = function() {\n $uibModal.open({\n backdrop: 'static',\n templateUrl: '/app/static/partials/staff/tools/trace/edit-options-modal.html',\n controller: 'StaffTraceEditOptionsCtrl',\n resolve: {\n enabledTraceOptions: function() {\n return $scope.enabledTraceOptions;\n },\n traceOptions: function() {\n return $scope.traceOptions;\n },\n forUser: function() {\n return true;\n },\n },\n }).result.then(function(s) {\n if (s) {\n var opts = [];\n angular.forEach(s.options, function(val, key) {\n if (val) opts.push(key);\n });\n if (opts.length == 0)\n opts = null;\n kwApi.SessionApiMetadata.traceUser({},{\n user_id: s.user_id,\n opts: opts,\n duration: s.duration,\n }).$promise.then(function(rv) {\n alertService.add({msg: \"Tracing will \" + (opts ? \"start\" : \"stop\") + \" on user's next message query\", type: \"info\"});\n }, function(err) {\n alertService.addApiError(err,'Trace failed');\n });\n }\n });\n };\n}])\n\n.controller('StaffTraceEditOptionsCtrl', [\"$scope\", \"enabledTraceOptions\", \"$filter\", \"traceOptions\", \"forUser\", \"kwApi\", function($scope, enabledTraceOptions, $filter, traceOptions, forUser, kwApi) {\n $scope.forUser = forUser;\n $scope.enabledTraceOptions = (forUser ? {} : angular.copy(enabledTraceOptions));\n $scope.traceOptions = traceOptions;\n $scope.duration = '300';\n\n $scope.patronSelectConfig = {\n load: function(query, callback) {\n kwApi.Patron.query({view: 'picker', searchValue: query}).$promise.then(function(rv) {\n callback(rv);\n }, function(err) {\n callback();\n });\n },\n maxItems: 1,\n loadThrottle: 600,\n valueField: 'borrowernumber',\n labelField: 'firstname',\n searchField: ['firstname','surname'],\n render: {\n item: function(item, escape) {\n if (!item) return '';\n var branchName = $filter('displayName')(item.branch_code, 'branch');\n return '
    ' + item.firstname + ' ' + item.surname + ' (' + branchName + ')
    ';\n },\n option: function(item, escape) {\n if (!item) return '';\n var branchName = $filter('displayName')(item.branch_code, 'branch');\n return '
    ' + item.firstname + ' ' + item.surname + ' (' + branchName + ')
    ';\n }\n }\n };\n\n $scope.$watch('enabledTraceOptions', function(newVal) {\n if (!newVal) return;\n $scope.disabled = {};\n $scope.allDisabledExcept = '';\n $scope.traceOptions.forEach(function(group) {\n group.items.forEach(function(item) {\n if (!newVal[item.value]) return;\n if (!item.implies) return;\n item.implies.forEach(function(v) {\n if (v == '*') {\n $scope.allDisabledExcept = item.value;\n }\n else {\n $scope.disabled[v] = true;\n }\n });\n });\n });\n }, true);\n\n}])\n\n.controller('StaffTraceViewCtrl', [\"$scope\", \"trace\", function($scope, trace) {\n $scope.trace = $scope.processTrace(trace);\n $scope.showData = true;\n $scope.order = {field: 'tot_time', reverse: true};\n}])\n\n.controller('StaffAdminCircBaseCtrl', [\"$scope\", \"$state\", \"userService\", \"configService\", function($scope, $state, userService, configService) {\n $scope.hdr = {\n title: 'Circ Admin',\n save: function() { ; },\n dirty: false,\n groupPolicies: ((configService.BranchGroupPolicies && (configService.BranchGroupPolicies !== null)) ? true : false),\n };\n\n $scope.setOptions = function() {\n $scope.hdr.state = $state.$current.name.split('.').pop();\n };\n\n $scope.setOptions();\n\n $scope.$on('$stateChangeSuccess', function(evnt, toState) {\n $scope.setOptions();\n });\n\n $scope.isClass = function(s) {\n return ($scope.hdr.state == s) ? 'btn-primary' : 'btn-outline-secondary';\n };\n\n $scope.permit = {\n all: false,\n branches: [],\n groups: [],\n any: false,\n };\n\n $scope.branchSelectConfig = {\n maxItems: 1,\n valueField: 'id',\n labelField: 'display', \n searchField: ['display'],\n create: false,\n };\n\n $scope.hdr.branch = userService.login_branch;\n\n $scope.getPermissions = function() {\n $scope.permit.all = userService.can({parameters: 'circ_policy'});\n\n userService.getAccessibleBranchesAndGroups('parameters.circ_policy').then(function(b) {\n $scope.permit.branches = b.branches;\n $scope.permit.groups = b.groups;\n $scope.permit.any = (b.branches.length>0);\n\n $scope.hdr.branches = [];\n Array.prototype.push.apply(\n $scope.hdr.branches,\n $scope.permit.branches.map(function(b) {\n return { id: b.branchcode, display: b.branchname }\n }).sort(function(a,b) {\n return (a.display < b.display) ? -1 :\n (a.display > b.display) ? 1 : 0;\n })\n );\n if ($scope.hdr.groupPolicies) {\n Array.prototype.push.apply(\n $scope.hdr.branches,\n $scope.permit.groups.map(function(c) {\n return {\n id: ('[BranchGroup]'+c.categorycode),\n display: ('[Group] ' + c.categoryname)\n }\n }).sort(function(a,b) {\n return (a.display < b.display) ? -1 :\n (a.display > b.display) ? 1 : 0;\n })\n );\n }\n\n if ($scope.permit.all) {\n $scope.hdr.branches.push({id: '==DEFAULT==', display: 'Default'});\n $scope.hdr.branches.push({id: '', display: 'All'});\n }\n });\n };\n\n $scope.getPermissions();\n $scope.$on('userSetBranch', $scope.getPermissions);\n\n $scope.hdr.getSearchArgs = function(baseArgs) {\n var args = angular.extend({}, (baseArgs || {}));\n if ($scope.hdr.branch === '==DEFAULT==') {\n args.default = 1;\n }\n else if (!$scope.hdr.branch || ($scope.hdr.branch == '')) {\n ;\n }\n else if ($scope.hdr.branch.substr(0,13) == \"[BranchGroup]\") {\n args.branch_group = $scope.hdr.branch.substr(13);\n }\n else {\n args.branch_code = $scope.hdr.branch;\n }\n return args;\n };\n\n $scope.hdr.getBranchCode = function() {\n if ($scope.hdr.branch === '==DEFAULT==') {\n return null;\n }\n else if (!$scope.hdr.branch || ($scope.hdr.branch == '')) {\n return null;\n }\n else if ($scope.hdr.branch.substr(0,13) == \"[BranchGroup]\") {\n return null;\n }\n else {\n return $scope.hdr.branch;\n }\n };\n\n $scope.hdr.getBranchGroup = function() {\n if ($scope.hdr.branch === '==DEFAULT==') {\n return null;\n }\n else if (!$scope.hdr.branch || ($scope.hdr.branch == '')) {\n return null;\n }\n else if ($scope.hdr.branch.substr(0,13) == \"[BranchGroup]\") {\n return $scope.hdr.branch.substr(13);\n }\n else {\n return null;\n }\n };\n\n \n}])\n\n.controller('StaffAdminCircConfigCtrl', [\"$scope\", \"circScopes\", \"circControl\", \"kwApi\", \"loading\", \"$q\", function($scope, circScopes, circControl, kwApi, loading, $q) {\n $scope.circScopes = circScopes;\n\n $scope.precedenceSelectConfig = {\n valueField: 'value',\n labelField: 'display', \n searchField: ['display'],\n create: false,\n };\n\n $scope.hdr.branchSelect = false;\n $scope.hdr.test = false;\n\n $scope.circControl = [];\n\n var deregister;\n var register = function() {\n deregister = $scope.$watch('circControl', function(newVal, oldVal) {\n if (!oldVal || angular.equals(newVal,oldVal))\n return;\n\n for (var i=0; i 0)) {\n if ($scope.hdr.branch === '==DEFAULT==') {\n $scope.policy.branch_code = null;\n $scope.policy.branch_group = null;\n }\n else if ($scope.hdr.branch.substr(0,13) == '[BranchGroup]') {\n $scope.policy.branch_code = null;\n $scope.policy.branch_group = $scope.hdr.branch.substr(13);\n }\n else if ($scope.hdr.branch) {\n $scope.policy.branch_code = $scope.hdr.branch;\n $scope.policy.branch_group = null;\n }\n else {\n $scope.policy.branch_code = null;\n $scope.policy.branch_group = null;\n }\n }\n\n\n $scope.parameters.forEach(function(p) {\n if (!p.enabled) return;\n\n if (p.unlimitedIfNull && (p.isLimited === '0')) {\n newPolicy[p.variable] = null;\n }\n else {\n newPolicy[p.variable] = p.value;\n }\n\n if (p.scopeOptions && (p.scope !== 'NONE')) {\n newPolicy[p.variable + '_scope'] = p.scope;\n }\n\n if (p.unitOptions) {\n newPolicy[p.variable + '_unit'] = p.units;\n }\n });\n $scope.policy.args = newPolicy;\n if ($scope.policy.id > 0) {\n loading.wrap(\n kwApi.CircPolicy.put({id: $scope.policy.id},$scope.policy).$promise,\n \"Unable to save circ policy\"\n ).then(function(rv) {\n $scope.reload();\n $scope.hdr.dirty = false;\n $scope.parameters.forEach(function(p) { p.dirty = false; });\n }, function(err) {\n $scope.reload();\n });\n }\n else {\n loading.wrap(\n kwApi.CircPolicy.save($scope.policy).$promise,\n \"Unable to save circ policy\"\n ).then(function(rv) {\n $scope.hdr.dirty = false;\n $scope.parameters.forEach(function(p) { p.dirty = false; });\n $state.go('staff.admin.circ.policy',{id: rv.id},{reload: true});\n }, function(err) {\n ;\n }); \n }\n };\n}])\n\n.controller('StaffAdminCircRulesCtrl', [\"$scope\", \"loading\", \"kwApi\", \"kohaDlg\", \"$uibModal\", function($scope, loading, kwApi, kohaDlg, $uibModal) {\n\n $scope.rules = [];\n $scope.hdr.branchSelect = true;\n $scope.hdr.branchSelectDisabled = false;\n $scope.reload = function() {\n var args = $scope.hdr.getSearchArgs();\n loading.wrap(\n kwApi.CircRule.query(args).$promise,\n \"Unable to load circ rules\"\n ).then(function(rv) {\n rv.forEach(function(rec) {\n try {\n rec.policy_args = JSON.parse(rec.policy_args);\n }\n catch (e) {\n ;\n }\n });\n $scope.rules = rv;\n }, function(err) {\n ;\n });\n };\n\n $scope.reload();\n\n $scope.$watch('hdr.branch', $scope.reload);\n\n $scope.delete = function(p) {\n kohaDlg.dialog({\n heading: 'Are you sure?',\n message: 'Are you sure you want to delete this rule? This CANNOT be undone.',\n buttons: [{val: true, label: 'Yes', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n }).result.then(function(rv) {\n if (!rv) return;\n loading.wrap(\n kwApi.CircRule.delete({id: p.id}).$promise,\n \"Can't delete circ rule\"\n ).then(function(rv) {\n $scope.reload();\n }, function(err) {\n $scope.reload();\n });\n });\n };\n\n $scope.edit = function(p) {\n var baseArgs = {default: 1};\n if (p.branch_code)\n baseArgs.branch_code = p.branch_code;\n else if (p.branch_group)\n baseArgs.branch_group = p.branch_group;\n $uibModal.open({\n backdrop: 'static',\n templateUrl: '/app/static/partials/staff/admin/circ/rule-modal.html',\n controller: 'StaffAdminCircRuleCtrl',\n resolve: {\n hdr: function() {\n return $scope.hdr;\n },\n rule: function() {\n return p;\n },\n permit: function() {\n return $scope.permit;\n },\n policies: function() {\n return kwApi.CircPolicy.query($scope.hdr.getSearchArgs(baseArgs)).$promise;\n },\n termsets: function() {\n return kwApi.CircTermset.query($scope.hdr.getSearchArgs(baseArgs)).$promise;\n },\n test: function() {\n return false;\n },\n },\n }).result.then(function(s) {\n $scope.reload();\n }, function(err) {\n $scope.reload();\n });\n };\n\n $scope.new = function() {\n $uibModal.open({\n backdrop: 'static',\n templateUrl: '/app/static/partials/staff/admin/circ/rule-modal.html',\n controller: 'StaffAdminCircRuleCtrl',\n resolve: {\n hdr: function() {\n return $scope.hdr;\n },\n rule: function() {\n return {\n branch_code: $scope.hdr.getBranchCode(),\n branch_group: $scope.hdr.getBranchGroup(), \n item_type: null,\n patron_category_code: null,\n };\n },\n permit: function() {\n return $scope.permit;\n },\n policies: function() {\n return kwApi.CircPolicy.query($scope.hdr.getSearchArgs({default: 1})).$promise;\n },\n termsets: function() {\n return kwApi.CircTermset.query($scope.hdr.getSearchArgs({default: 1})).$promise;\n },\n test: function() {\n return false;\n },\n },\n }).result.then(function(s) {\n $scope.reload();\n }, function(err) {\n $scope.reload();\n });\n };\n\n $scope.hdr.test = function() {\n $uibModal.open({\n backdrop: 'static',\n templateUrl: '/app/static/partials/staff/admin/circ/rule-test-modal.html',\n controller: 'StaffAdminCircRuleCtrl',\n size: 'lg',\n resolve: {\n hdr: function() {\n return $scope.hdr;\n },\n rule: function() {\n return {\n branch_code: $scope.hdr.getBranchCode(),\n branch_group: $scope.hdr.getBranchGroup(), \n item_type: null,\n patron_category_code: null,\n };\n },\n permit: function() {\n return $scope.permit;\n },\n policies: function() {\n return [];\n },\n termsets: function() {\n return [];\n },\n test: function() {\n return true;\n },\n },\n });\n };\n}])\n\n.controller('StaffAdminCircRuleCtrl', [\"$scope\", \"permit\", \"test\", \"rule\", \"policies\", \"termsets\", \"kwApi\", \"loading\", \"configService\", \"hdr\", function($scope, permit, test, rule, policies, termsets, kwApi, loading, configService, hdr) {\n $scope.rule = rule;\n $scope.test = test;\n $scope.hdr = hdr;\n if (test) $scope.rule.branch_group = false;\n\n $scope.policies = policies;\n $scope.termsets = termsets;\n\n $scope.selectConfig = {\n maxItems: 1,\n valueField: 'value',\n labelField: 'display', \n searchField: ['display'],\n create: false,\n };\n\n $scope.policySelectConfig = {\n maxItems: 1,\n valueField: 'id',\n labelField: 'description', \n searchField: ['description'],\n create: false,\n };\n\n $scope.onBranchChange = function() {\n var args = {};\n if ($scope.rule.branch_code) {\n args = {branch_code: $scope.rule.branch_code, default: 1};\n }\n else if ($scope.rule.branch_group) {\n args = {branch_group: $scope.rule.branch_group, default: 1};\n }\n else {\n args = {default: 1};\n }\n\n loading.wrap(\n kwApi.CircPolicy.query(args).$promise,\n \"Unable to load circ policies\"\n ).then(function(rv) {\n $scope.policies = rv;\n });\n\n loading.wrap(\n kwApi.CircTermset.query(args).$promise,\n \"Unable to load circ termsets\"\n ).then(function(rv) {\n $scope.termsets = rv;\n });\n };\n\n $scope.$watch('rule', function(newVal, oldVal) {\n if (!(newVal && !angular.equals(newVal,oldVal))) return;\n\n if (!$scope.test) {\n if (newVal.branch_code !== '' && newVal.branch_group !== '') {\n if (oldVal.branch_code === '' && newVal.branch_code !== '') {\n newVal.branch_group = '';\n }\n else if (oldVal.branch_group === '' && newVal.branch_group !== '') {\n newVal.branch_code= '';\n }\n else {\n newVal.branch_group = '';\n }\n }\n\n if ((newVal.branch_code !== oldVal.branch_code) || (newVal.branch_group !== oldVal.branch_group)) {\n $scope.onBranchChange();\n }\n }\n else {\n if (newVal.branch_code && newVal.item_type && newVal.patron_category_code) {\n loading.wrap(\n kwApi.CircRule.testTuple(newVal).$promise,\n \"Unable to test\"\n ).then(function(rv) {\n var cascaded = {};\n for (var i=0; i b.display ? 1 : 0) });\n if (!$scope.test)\n $scope.branches.unshift({value: '', display: '(Any)'});\n\n $scope.branch_groups = permit.groups\n .map(function(s) { return {value: s.categorycode, display: s.categoryname}; })\n .sort(function(a,b) { return (a.display < b.display ? -1 : a.display > b.display ? 1 : 0) });\n if (!$scope.test)\n $scope.branch_groups.unshift({value: '', display: '(Any)'});\n\n $scope.item_types = Object.keys(configService.itemtypes)\n .map(function(p) { return { value: p, display: configService.itemtypes[p].description }; })\n .sort(function(a,b) { return (a.display < b.display ? -1 : a.display > b.display ? 1 : 0) });\n if (!$scope.test)\n $scope.item_types.unshift({value: '', display: '(Any)'});\n\n $scope.patron_categories = Object.keys(configService.patroncats)\n .map(function(p) { return { value: p, display: configService.patroncats[p].description }; })\n .sort(function(a,b) { return (a.display < b.display ? -1 : a.display > b.display ? 1 : 0) });\n if (!$scope.test)\n $scope.patron_categories.unshift({value: '', display: '(Any)'});\n\n $scope.save = function() {\n if ($scope.test) return;\n\n var newRule = angular.copy($scope.rule);\n ['branch_code','branch_group','item_type','patron_category_code','circ_policy_id','circ_termset_id'].forEach(function(p) {\n if (newRule[p] === '')\n newRule[p] = null;\n });\n\n if (newRule.id) {\n loading.wrap(\n kwApi.CircRule.put({id: newRule.id},newRule).$promise,\n \"Unable to save rule\"\n ).then(function(rv) {\n $scope.$close();\n });\n }\n else {\n loading.wrap(\n kwApi.CircRule.save(newRule).$promise,\n \"Unable to save rule\"\n ).then(function(rv) {\n $scope.$close();\n });\n }\n };\n}])\n\n\n.controller('StaffAdminCircRecallRulesCtrl', [\"$scope\", \"loading\", \"kwApi\", \"kohaDlg\", \"$uibModal\", function($scope, loading, kwApi, kohaDlg, $uibModal) {\n\n $scope.rules = [];\n $scope.hdr.branchSelect = true;\n $scope.hdr.branchSelectDisabled = false;\n $scope.reload = function() {\n var args = $scope.hdr.getSearchArgs();\n loading.wrap(\n kwApi.RecallRule.query(args).$promise,\n \"Unable to load circ rules\"\n ).then(function(rv) {\n rv.forEach(function(rec) {\n try {\n rec.policy_args = JSON.parse(rec.policy_args);\n }\n catch (e) {\n ;\n }\n });\n $scope.rules = rv;\n }, function(err) {\n ;\n });\n };\n\n $scope.reload();\n\n $scope.$watch('hdr.branch', $scope.reload);\n\n $scope.delete = function(p) {\n kohaDlg.dialog({\n heading: 'Are you sure?',\n message: 'Are you sure you want to delete this rule? This CANNOT be undone.',\n buttons: [{val: true, label: 'Yes', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n }).result.then(function(rv) {\n if (!rv) return;\n loading.wrap(\n kwApi.RecallRule.delete({id: p.id}).$promise,\n \"Can't delete circ rule\"\n ).then(function(rv) {\n $scope.reload();\n }, function(err) {\n $scope.reload();\n });\n });\n };\n\n $scope.edit = function(p) {\n $uibModal.open({\n backdrop: 'static',\n templateUrl: '/app/static/partials/staff/admin/circ/recall-rule-modal.html',\n controller: 'StaffAdminCircRecallRuleCtrl',\n resolve: {\n hdr: function() {\n return $scope.hdr;\n },\n rule: function() {\n return p;\n },\n permit: function() {\n return $scope.permit;\n },\n policies: function() {\n return kwApi.CircPolicy.query($scope.hdr.getSearchArgs({default: 1})).$promise;\n },\n test: function() {\n return false;\n },\n },\n }).result.then(function(s) {\n $scope.reload();\n }, function(err) {\n $scope.reload();\n });\n };\n\n $scope.new = function() {\n $uibModal.open({\n backdrop: 'static',\n templateUrl: '/app/static/partials/staff/admin/circ/recall-rule-modal.html',\n controller: 'StaffAdminCircRecallRuleCtrl',\n resolve: {\n hdr: function() {\n return $scope.hdr;\n },\n rule: function() {\n return {\n branch_code: $scope.hdr.getBranchCode(),\n branch_group: $scope.hdr.getBranchGroup(), \n item_type: null,\n patron_category_code: null,\n };\n },\n permit: function() {\n return $scope.permit;\n },\n policies: function() {\n return kwApi.CircPolicy.query($scope.hdr.getSearchArgs({default: 1})).$promise;\n },\n test: function() {\n return false;\n },\n },\n }).result.then(function(s) {\n $scope.reload();\n }, function(err) {\n $scope.reload();\n });\n };\n\n $scope.hdr.test = function() {\n $uibModal.open({\n backdrop: 'static',\n templateUrl: '/app/static/partials/staff/admin/circ/recall-rule-test-modal.html',\n controller: 'StaffAdminCircRecallRuleCtrl',\n size: 'lg',\n resolve: {\n hdr: function() {\n return $scope.hdr;\n },\n rule: function() {\n return {\n branch_code: $scope.hdr.getBranchCode(),\n branch_group: $scope.hdr.getBranchGroup(), \n item_type: null,\n patron_category_code: null,\n };\n },\n permit: function() {\n return $scope.permit;\n },\n policies: function() {\n return [];\n },\n test: function() {\n return true;\n },\n },\n });\n };\n}])\n\n.controller('StaffAdminCircRecallRuleCtrl', [\"$scope\", \"permit\", \"test\", \"rule\", \"policies\", \"kwApi\", \"loading\", \"configService\", \"hdr\", function($scope, permit, test, rule, policies, kwApi, loading, configService, hdr) {\n $scope.rule = rule;\n $scope.test = test;\n $scope.hdr = hdr;\n if (test) $scope.rule.branch_group = false;\n\n $scope.policies = policies;\n\n $scope.selectConfig = {\n maxItems: 1,\n valueField: 'value',\n labelField: 'display', \n searchField: ['display'],\n create: false,\n };\n\n $scope.policySelectConfig = {\n maxItems: 1,\n valueField: 'id',\n labelField: 'description', \n searchField: ['description'],\n create: false,\n };\n\n $scope.onBranchChange = function() {\n var args = {};\n if ($scope.rule.branch_code) {\n args = {branch_code: $scope.rule.branch_code, default: 1};\n }\n else if ($scope.rule.branch_group) {\n args = {branch_group: $scope.rule.branch_group, default: 1};\n }\n else {\n args = {default: 1};\n }\n\n loading.wrap(\n kwApi.CircPolicy.query(args).$promise,\n \"Unable to load circ policies\"\n ).then(function(rv) {\n $scope.policies = rv;\n });\n };\n\n $scope.$watch('rule', function(newVal, oldVal) {\n if (!(newVal && !angular.equals(newVal,oldVal))) return;\n\n if (!$scope.test) {\n if (newVal.branch_code !== '' && newVal.branch_group !== '') {\n if (oldVal.branch_code === '' && newVal.branch_code !== '') {\n newVal.branch_group = '';\n }\n else if (oldVal.branch_group === '' && newVal.branch_group !== '') {\n newVal.branch_code= '';\n }\n else {\n newVal.branch_group = '';\n }\n }\n\n if ((newVal.branch_code !== oldVal.branch_code) || (newVal.branch_group !== oldVal.branch_group)) {\n $scope.onBranchChange();\n }\n }\n else {\n if (newVal.branch_code && newVal.item_type && newVal.patron_category_code) {\n loading.wrap(\n kwApi.RecallRule.testTuple(newVal).$promise,\n \"Unable to test\"\n ).then(function(rv) {\n for (var i=0; i b.display ? 1 : 0) });\n if (!$scope.test)\n $scope.branches.unshift({value: '', display: '(Any)'});\n\n $scope.branch_groups = permit.groups\n .map(function(s) { return {value: s.categorycode, display: s.categoryname}; })\n .sort(function(a,b) { return (a.display < b.display ? -1 : a.display > b.display ? 1 : 0) });\n if (!$scope.test)\n $scope.branch_groups.unshift({value: '', display: '(Any)'});\n\n $scope.item_types = Object.keys(configService.itemtypes)\n .map(function(p) { return { value: p, display: configService.itemtypes[p].description }; })\n .sort(function(a,b) { return (a.display < b.display ? -1 : a.display > b.display ? 1 : 0) });\n if (!$scope.test)\n $scope.item_types.unshift({value: '', display: '(Any)'});\n\n $scope.from_opac = [{ value: 0, display: 'No'}, { value: 1, display: 'Yes' }];\n\n $scope.patron_categories = Object.keys(configService.patroncats)\n .map(function(p) { return { value: p, display: configService.patroncats[p].description }; })\n .sort(function(a,b) { return (a.display < b.display ? -1 : a.display > b.display ? 1 : 0) });\n if (!$scope.test)\n $scope.patron_categories.unshift({value: '', display: '(Any)'});\n\n $scope.save = function() {\n if ($scope.test) return;\n\n var newRule = angular.copy($scope.rule);\n ['branch_code','branch_group','item_type','patron_category_code','circ_policy_id'].forEach(function(p) {\n if (newRule[p] === '')\n newRule[p] = null;\n });\n\n if (newRule.id) {\n loading.wrap(\n kwApi.RecallRule.put({id: newRule.id},newRule).$promise,\n \"Unable to save rule\"\n ).then(function(rv) {\n $scope.$close();\n });\n }\n else {\n loading.wrap(\n kwApi.RecallRule.save(newRule).$promise,\n \"Unable to save rule\"\n ).then(function(rv) {\n $scope.$close();\n });\n }\n };\n}])\n\n\n.controller('StaffAdminCircTermsetsCtrl', [\"$scope\", \"$state\", \"loading\", \"kwApi\", \"kohaDlg\", function($scope, $state, loading, kwApi, kohaDlg) {\n\n $scope.termsets = [];\n $scope.hdr.branchSelect = true;\n $scope.hdr.branchSelectDisabled = false;\n $scope.hdr.test = false;\n $scope.hdr.save = false;\n\n $scope.reload = function() {\n var args = $scope.hdr.getSearchArgs();;\n loading.wrap(\n kwApi.CircTermset.query(args).$promise,\n \"Unable to load circ termsets\"\n ).then(function(rv) {\n $scope.termsets = rv;\n }, function(err) {\n ;\n });\n };\n\n $scope.reload();\n\n $scope.$watch('hdr.branch', $scope.reload);\n\n $scope.delete = function(p) {\n kohaDlg.dialog({\n heading: 'Are you sure?',\n message: 'Are you sure you want to delete this term? This CANNOT be undone.',\n buttons: [{val: true, label: 'Yes', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n }).result.then(function(rv) {\n if (!rv) return;\n loading.wrap(\n kwApi.CircTermset.delete({id: p.id}).$promise,\n \"Can't delete circ policy\"\n ).then(function(rv) {\n $scope.reload();\n }, function(err) {\n $scope.reload();\n });\n });\n };\n\n $scope.edit = function(p) {\n $state.go('staff.admin.circ.termset', {id: p.id});\n };\n\n $scope.new = function(p) {\n $state.go('staff.admin.circ.termset', {id: 0});\n };\n}])\n\n.controller('StaffAdminCircTermsetCtrl', [\"$scope\", \"$q\", \"$state\", \"termset\", \"termDates\", \"loading\", \"kwApi\", \"kohaDlg\", \"$uibModal\", function($scope, $q, $state, termset, termDates, loading, kwApi, kohaDlg, $uibModal) {\n $scope.hdr.test = false;\n\n $scope.refresh = function(p) {\n $scope.termset = p[0];\n $scope.termDates = p[1];\n };\n\n $scope.reload = function() {\n loading.wrap(\n $q.all([\n kwApi.CircTermset.get({id: $scope.termset.id}).$promise,\n kwApi.CircTermset.getDates({id: $scope.termset.id}).$promise,\n ]),\n \"Unable to load term set\"\n ).then(function(rv) {\n $scope.refresh(rv);\n }, function(err) {\n ;\n });\n };\n $scope.refresh([termset,termDates]);\n\n\n $scope.hdr.branchSelect = true;\n if ($scope.termset.id) {\n $scope.hdr.branchSelectDisabled = true;\n if ($scope.termset.branch_code) {\n $scope.hdr.branch = $scope.termset.branch_code;\n }\n else if ($scope.termset.branch_group) {\n $scope.hdr.branch = '[BranchGroup]' + $scope.termset.branch_group;\n }\n else {\n $scope.hdr.branch = '==DEFAULT==';\n }\n }\n else {\n $scope.hdr.branchSelectDisabled = false;\n }\n\n $scope.$watch('termset.description', function(newVal, oldVal) {\n if (newVal && oldVal && newVal !== oldVal) {\n $scope.hdr.dirty = true;\n }\n });\n\n $scope.hdr.save = function() {\n if (!$scope.hdr.dirty) return;\n if ($scope.termset.id) {\n loading.wrap(\n kwApi.CircTermset.put({id: $scope.termset.id}, $scope.termset).$promise,\n \"Unable to save circ termset\"\n ).then(function(rv) {\n $scope.hdr.dirty = false;\n $scope.reload();\n }, function(rv) {\n $scope.reload();\n });\n }\n else {\n loading.wrap(\n kwApi.CircTermset.save($scope.termset).$promise,\n \"Unable to save circ termset\"\n ).then(function(rv) {\n $scope.hdr.dirty = false;\n $state.go('staff.admin.circ.termset', {id: rv.id}, {reload: true});\n }, function(rv) {\n $scope.reload();\n });\n }\n };\n\n $scope.edit = function(p) {\n $uibModal.open({\n backdrop: 'static',\n templateUrl: '/app/static/partials/staff/admin/circ/term-date-modal.html',\n controller: 'StaffAdminCircTermDateCtrl',\n resolve: {\n hdr: function() {\n return $scope.hdr;\n },\n termDate: function() {\n return p;\n },\n },\n }).result.then(function(s) {\n $scope.reload();\n }, function(err) {\n $scope.reload();\n });\n };\n\n $scope.new = function() {\n $uibModal.open({\n backdrop: 'static',\n templateUrl: '/app/static/partials/staff/admin/circ/term-date-modal.html',\n controller: 'StaffAdminCircTermDateCtrl',\n resolve: {\n hdr: function() {\n return $scope.hdr;\n },\n termDate: function() {\n return {circ_termset_id: $scope.termset.id};\n },\n },\n }).result.then(function(s) {\n $scope.reload();\n }, function(err) {\n $scope.reload();\n });\n };\n\n $scope.delete = function(p) {\n kohaDlg.dialog({\n heading: 'Are you sure?',\n message: 'Are you sure you want to delete this date? This CANNOT be undone.',\n buttons: [{val: true, label: 'Yes', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n }).result.then(function(rv) {\n if (!rv) return;\n loading.wrap(\n kwApi.CircTermDate.delete({id: p.id}).$promise,\n \"Can't delete date\"\n ).then(function(rv) {\n $scope.reload();\n }, function(err) {\n $scope.reload();\n });\n });\n };\n}])\n\n.controller('StaffAdminCircTermDateCtrl', [\"$scope\", \"termDate\", \"loading\", \"kwApi\", \"hdr\", function($scope, termDate, loading, kwApi, hdr) {\n $scope.hdr = hdr;\n $scope.date = angular.copy(termDate);\n if ($scope.date.id) {\n $scope.date.startdate = dayjs($scope.date.startdate).toDate();\n $scope.date.enddate = dayjs($scope.date.enddate).toDate();\n $scope.date.duedate = dayjs($scope.date.duedate).toDate();\n }\n\n $scope.$watch('date', function(newVal) {\n if (!newVal) return;\n var err = [];\n if (newVal.startdate > newVal.enddate) {\n err.push(\"Start date is after end date\");\n }\n if (newVal.enddate > newVal.duedate) {\n err.push(\"End date is after due date\");\n }\n $scope.valid = (err.length == 0) ? true : false;\n $scope.errors = err.join(', ');\n console.dir($scope.valid);\n console.dir($scope.errors);\n }, true);\n\n\n $scope.save = function() {\n loading.wrap(\n $scope.date.id\n ? kwApi.CircTermDate.put({id: $scope.date.id}, $scope.date).$promise\n : kwApi.CircTermDate.save($scope.date).$promise,\n \"Unable to save date\"\n ).then(function(rv) {\n $scope.$close();\n });\n };\n}])\n\n\n.controller('StaffAdminNcipBaseCtrl', [ \"$scope\", \"$state\", \"userService\", function($scope, $state, userService) {\n $scope.hdr = {\n title: 'NCIP Admin',\n save: function() { ; },\n dirty: false,\n };\n\n $scope.setOptions = function() {\n $scope.hdr.state = $state.$current.name.split('.').pop();\n };\n\n $scope.setOptions();\n\n $scope.$on('$stateChangeSuccess', function(evnt, toState) {\n $scope.setOptions();\n });\n\n $scope.isClass = function(s) {\n return ($scope.hdr.state == s) ? 'btn-primary' : 'btn-default';\n };\n\n $scope.permit = {\n all: false,\n branches: [],\n groups: [],\n any: false,\n };\n\n $scope.branchSelectConfig = {\n maxItems: 1,\n valueField: 'id',\n labelField: 'display', \n searchField: ['display'],\n create: false,\n };\n\n $scope.hdr.branch = userService.login_branch;\n\n $scope.getPermissions = function() {\n $scope.permit.all = userService.can({parameters: 'netsvc'});\n\n userService.getAccessibleBranchesAndGroups('parameters.netsvc').then(function(b) {\n $scope.permit.branches = b.branches;\n $scope.permit.groups = b.groups;\n $scope.permit.any = (b.branches.length>0);\n\n $scope.hdr.branches = [];\n Array.prototype.push.apply(\n $scope.hdr.branches,\n $scope.permit.branches.map(function(b) {\n return { id: b.branchcode, display: b.branchname }\n }).sort(function(a,b) {\n return (a.display < b.display) ? -1 :\n (a.display > b.display) ? 1 : 0;\n })\n );\n });\n };\n\n $scope.getPermissions();\n $scope.$on('userSetBranch', $scope.getPermissions);\n}])\n\n\n.controller('StaffAdminNcipAgenciesCtrl', [ \"$scope\", \"$state\", \"loading\", \"kwApi\", \"kohaDlg\", \n function($scope, $state, loading, kwApi, kohaDlg) {\n\n $scope.ncipAgencies = [];\n $scope.hdr.branchSelect = true;\n $scope.hdr.branchSelectDisabled = false;\n $scope.hdr.save = false;\n\n $scope.reload = function() {\n loading.wrap(\n kwApi.Branch.getNcipAgencies({id: $scope.hdr.branch}).$promise,\n \"Unable to load NCIP agencies\"\n ).then(function(rv) {\n $scope.ncipAgencies = rv;\n }, function(err) {\n ;\n });\n };\n\n $scope.reload();\n\n $scope.$watch('hdr.branch', $scope.reload);\n\n $scope.delete = function(p) {\n kohaDlg.dialog({\n heading: 'Are you sure?',\n message: 'Are you sure you want to delete this NCIP agency? This CANNOT be undone.',\n buttons: [{val: true, label: 'Yes', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n }).result.then(function(rv) {\n if (!rv) return;\n loading.wrap(\n kwApi.NcipAgency.delete({id: p.id}).$promise,\n \"Can't delete NCIP agency\"\n ).then(function(rv) {\n $scope.reload();\n }, function(err) {\n $scope.reload();\n });\n });\n };\n\n $scope.edit = function(p) {\n $state.go('staff.admin.ncip.agency', {id: p.id});\n };\n\n $scope.new = function(p) {\n $state.go('staff.admin.ncip.agency', {id: 0});\n };\n}])\n\n.controller('StaffAdminNcipAgencyCtrl', [ \"$scope\", \"$state\", \"ncipAgency\", \"loading\", \"kwApi\", function( $scope, $state, ncipAgency, loading, kwApi ) {\n\n $scope.knownServices = [\n 'LookupVersion',\n 'LookupAgency',\n 'LookupUser',\n 'CheckOutItem',\n 'CheckInItem',\n 'RenewItem',\n 'RequestItem',\n 'CancelRequestItem',\n ];\n\n $scope.hasService = {};\n\n $scope.refresh = function(p) {\n $scope.ncipAgency = p;\n $scope.ncipAgency.services ||= '';\n $scope.knownServices.forEach(function(s) {\n $scope.hasService[s] = false;\n });\n $scope.ncipAgency.services.split(' ').forEach(function(s) {\n if (s) {\n $scope.hasService[s] = true;\n }\n });\n };\n\n $scope.reload = function() {\n loading.wrap(\n kwApi.NcipAgency.get({id: $scope.ncipAgency.id}).$promise\n ).then(function(rv) {\n $scope.refresh(rv);\n }, function(err) {\n ;\n });\n };\n $scope.refresh(ncipAgency);\n\n $scope.hdr.branchSelect = true;\n if ($scope.ncipAgency.id) {\n $scope.hdr.branchSelectDisabled = true;\n if ($scope.ncipAgency.branch_code) {\n $scope.hdr.branch = $scope.ncipAgency.branch_code;\n }\n }\n else {\n $scope.hdr.branchSelectDisabled = false;\n }\n\n ['description','agency_id','system_id','agency_auth','system_auth'].forEach(function(p) {\n $scope.$watch('ncipAgency.'+p, function(newVal, oldVal) {\n if (newVal && oldVal && newVal !== oldVal) {\n $scope.hdr.dirty = true;\n }\n });\n });\n\n $scope.$watch('hasService', function(newVal, oldVal) {\n if (newVal && oldVal && !angular.equals(oldVal,newVal)) {\n $scope.hdr.dirty = true;\n\n $scope.ncipAgency.services = \n Object.keys($scope.hasService)\n .filter(function(f) { return $scope.hasService[f] })\n .sort(function(a,b) { return(ab ? 1 : 0) })\n .join(' ');\n }\n }, true);\n\n $scope.hdr.save = function() {\n if (!$scope.hdr.dirty) return;\n if ($scope.ncipAgency.id) {\n loading.wrap(\n kwApi.NcipAgency.put({id: $scope.ncipAgency.id}, $scope.ncipAgency).$promise,\n \"Unable to save NCIP agency\"\n ).then(function(rv) {\n $scope.hdr.dirty = false;\n $scope.reload();\n $state.go('staff.admin.ncip.agencies');\n }, function(rv) {\n $scope.reload();\n });\n }\n else {\n $scope.ncipAgency.branch_code = $scope.hdr.branch;\n loading.wrap(\n kwApi.NcipAgency.save($scope.ncipAgency).$promise,\n \"Unable to save NCIP agency\"\n ).then(function(rv) {\n $scope.hdr.dirty = false;\n $state.go('staff.admin.ncip.agencies');\n }, function(rv) {\n $scope.reload();\n });\n }\n };\n}])\n\n\n// Marc Validation Rulesets\n.controller('StaffMvrCtrl', [\"$scope\", \"$state\", \"userService\", function ($scope, $state, userService) {\n if ($state.current.name == 'staff.tools.mvr') {\n $state.go('staff.tools.mvr.index');\n }\n\n $scope.userCan = {\n modify: userService.can({editcatalogue: {mvr: 'update'}}),\n delete: userService.can({editcatalogue: {mvr: 'update'}}),\n };\n}])\n\n.controller('StaffMvrIndexCtrl', [\"$scope\", \"rulesets\", \"kwApi\", \"alertService\", \"kohaDlg\", function ($scope, rulesets, kwApi, alertService, kohaDlg) {\n $scope.rulesets = rulesets;\n\n $scope.order = {field: 'title', reverse: false};\n\n $scope.currentPage = 1;\n $scope.totalRows = (rulesets[0]||{})._embed_total_rows||0;\n\n $scope.reload = function() {\n var start = ($scope.currentPage -1) * 20;\n kwApi.MarcValidationRuleset.query({view: 'brief', start: start, limit: 20, sort: $scope.order.field, reverse: ($scope.order.reverse ? 1 : 0)}).$promise.then(function(rv) {\n $scope.rulesets = rv;\n });\n };\n\n $scope.pageChanged = function() {\n $scope.reload();\n };\n\n $scope.sortChanged = function() {\n $scope.currentPage = 1;\n $scope.reload();\n };\n\n $scope.delete = function(r) {\n kohaDlg.dialog({\n heading: 'Are you sure?',\n message: 'Are you sure you want to delete this ruleset? This CANNOT be undone.',\n buttons: [{val: true, label: 'Yes', btnClass: 'btn-primary'}, {val: false, label: 'Cancel'}],\n }).result.then(function(rv) {\n if (rv) {\n kwApi.MarcValidationRuleset.delete({id: r.id}).$promise.then(function(rv) {\n alertService.add({msg: \"Ruleset deleted\"});\n $scope.reload();\n }, function(err) {\n alertService.addApiError(err, 'Delete failed');\n $scope.reload();\n });\n }\n });\n };\n}])\n\n.controller('StaffMvrDetailsCtrl', [\"$scope\", \"ruleset\", \"kwApi\", \"$state\", \"loading\", \"$uibModal\", function ($scope, ruleset, kwApi, $state, loading, $uibModal) {\n $scope.ruleset = ruleset;\n $scope.editing = ($scope.ruleset.id ? false : true);\n $scope.editingBlock = false;\n $scope.title = ruleset.title;\n $scope.syntaxClass = 'warning';\n $scope.blockIndex = 0;\n $scope.blockRef = {};\n\n $scope.refresh = function() {\n $scope.title = ruleset.title;\n $scope.syntaxClass = 'success';\n $scope.ruleset.blocks.forEach(function(b) {\n if (!b._id) {\n b._id = ++$scope.blockIndex;\n }\n if (b._syntaxClass == 'danger')\n $scope.syntaxClass = 'danger';\n else if (b._syntaxClass == 'warning' && $scope.syntaxClass == 'success')\n $scope.syntaxClass = 'warning';\n\n });\n };\n\n $scope.reload = function() {\n if ($scope.ruleset.id) {\n kwApi.MarcValidationRuleset.get({id: $scope.ruleset.id}).$promise.then(function(rv) {\n $scope.ruleset = rv;\n $scope.refresh();\n });\n }\n };\n\n if ($scope.ruleset.id) {\n $scope.refresh();\n }\n else {\n $scope.ruleset = {blocks: [{code: ''}]};\n }\n\n $scope.edit = function() {\n if ($scope.userCan.modify) {\n $scope.editing = true;\n }\n };\n\n $scope.editCancel = function() {\n if ($scope.ruleset.id) {\n $scope.editing = false;\n $scope.reload();\n }\n else {\n $state.go('staff.tools.mvr.index');\n }\n };\n\n $scope.editBlock = function(n) {\n $scope.editingBlock = n;\n var b = $scope.ruleset.blocks[n];\n b._edit = true;\n b._old = b.code;\n };\n\n $scope.saveBlock = function(n) {\n $scope.editingBlock = false;\n var b = $scope.ruleset.blocks[n];\n if (b.code !== b._old) {\n b.meta.dirty = true;\n b._syntaxClass = 'warning';\n $scope.syntaxClass = 'warning';\n $scope.anyDirty = true;\n }\n b._edit = false;\n };\n\n $scope.deleteBlock = function(n) {\n $scope.ruleset.blocks.splice(n,1);\n };\n\n $scope.newBlock = function() {\n $scope.ruleset.blocks.push({code: '', _syntaxClass: 'warning', _id: ++$scope.blockIndex});\n };\n\n $scope.checkBlock = function(n) {\n var b = $scope.ruleset.blocks[n];\n kwApi.MarcValidationRuleset.checkSyntax({}, {code: b.code}).$promise\n .then(function(rv) {\n if (rv.error) {\n b._error = rv.error\n b._syntaxClass = 'danger';\n }\n else {\n b._error = null;\n b._syntaxClass = 'success';\n }\n });\n };\n\n $scope.buildBlock = function(n) {\n $uibModal.open({\n backdrop: 'static',\n templateUrl: '/app/static/partials/staff/tools/mvr/builder-modal.html',\n controller: 'StaffMvrBuilderCtrl',\n size: 'lg',\n resolve: {\n block: function() {\n return $scope.ruleset.blocks[n];\n },\n },\n }).result.then(function(s) {\n if (s) {\n $scope.anyDirty = true;\n $scope.ruleset.blocks[n] = s;\n }\n });\n };\n\n $scope.checkAll = function() {\n kwApi.MarcValidationRuleset.checkAllSyntax({id: $scope.ruleset.id},{}).$promise.then(function(rv) {\n $scope.syntaxClass = 'success';\n var i;\n for (i=0; i' + item.firstname + ' ' + item.surname + ' [' + item.card_number + '] (' + branchName + ')
    ';\n },\n option: function(item, escape) {\n if (!item) return '';\n var branchName = $filter('displayName')(item.branch_code, 'branch');\n return '
    ' + item.firstname + ' ' + item.surname + ' [' + item.card_number + '] (' + branchName + ')
    ';\n }\n }\n };\n\n $scope.addUser = function() {\n kwApi.Patron.get({id: $scope.m.new_user_id}).$promise.then(function(rv) {\n var branchName = $filter('displayName')(rv.branchcode, 'branch');\n $scope.m.users.push({\n id: rv.borrowernumber,\n description: (rv.firstname + ' ' + rv.surname + ' (' + branchName + ') [' + rv.cardnumber + ']'),\n });\n $scope.m.new_user_id = null;\n });\n };\n\n $scope.delUser = function(n) {\n $scope.m.users.splice(n,1);\n };\n\n $scope.send = function() {\n var message = {};\n var opts = {};\n var async = false;\n\n if (!$scope.m.type) return;\n message.code = $scope.m.type;\n\n var users = $scope.m.users.map(function(e) { return e.id; });\n if ($scope.m.new_user_id !== null)\n users.push($scope.m.new_user_id);\n\n if (($scope.m.recipientType !== 'multicast') && !users.length && !$scope.m.email) return;\n\n if ($scope.m.type === 'gen.letter') {\n message.parameters = {\n title: $scope.m.title,\n content: $scope.m.content,\n };\n\n if ($scope.m.recipientType === 'users') {\n if (users.length > 1)\n message.user_ids = users;\n else if (users.length == 1)\n message.user_id = users[0];\n else\n return;\n }\n else if ($scope.m.recipientType === 'email') {\n if (!$scope.m.email) return;\n message.recipient = $scope.m.email;\n }\n else {\n opts.multicast = 1;\n }\n\n if (!opts.force_endpoint && ($scope.m.endpoint !== 'default'))\n opts.force_endpoint = $scope.m.endpoint;\n else if ($scope.m.recipientType === 'email')\n opts.force_endoint = 'email';\n\n if ($scope.m.immediate)\n opts.deliver = 1;\n\n if ($scope.m.priority)\n message.level = 2;\n\n if ($scope.m.async === 1 || $scope.m.async === \"1\")\n async = true;\n }\n else if ($scope.m.type === 'gen.misc') {\n message.parameters = $scope.m.content;\n\n if ($scope.m.recipientType === 'users') {\n if (users.length > 1)\n message.user_ids = users;\n else if (users.length == 1)\n message.user_id = users[0];\n else\n return;\n }\n else {\n opts.multicast = 1;\n }\n\n if (!opts.force_endpoint && ($scope.m.endpoint !== 'default'))\n opts.force_endpoint = $scope.m.endpoint;\n\n if ($scope.m.immediate)\n opts.deliver = 1;\n\n if ($scope.m.priority)\n message.level = 2;\n\n if ($scope.m.async === 1 || $scope.m.async === \"1\")\n async = true;\n }\n else {\n message.parameters = {\n shutdown: { when: $scope.m.when }\n };\n\n opts.multicast = 1;\n opts.force_endpoint = 'popup';\n message.level = 2;\n\n if ($scope.m.async === 1 || $scope.m.async === \"1\")\n async = true;\n }\n\n var reqParams = {};\n if (async)\n reqParams.async = 1;\n\n loading.wrap(\n kwApi.Message.send(reqParams, {message: message, opts: opts}).$promise,\n \"Unable to send message\"\n ).then(function(rv) {\n if (async)\n alertService.add({msg: \"Message enqueued for asynchronous delivery\", type: 'info'});\n else\n alertService.add({msg: \"Sent \" + rv.messages + \" messages to \" + rv.endpoints + \" endpoints\", type: 'info'});\n });\n };\n}])\n\n})();\n","(function () {\n'use strict';\n\nif (!Array.prototype.replaceWith) {\n Array.prototype.replaceWith = function(array) {\n this.length = 0;\n array.forEach(function(v) { this.push(v)}, this);\n };\n}\n\n\nvar SPECIAL_CHARS_REGEXP = /([\\:\\-\\_]+(.))/g;\nfunction camelCase(name) {\n return name.\n replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {\n return offset ? letter.toUpperCase() : letter;\n });\n}\n\n\nvar parseBooleanOption = function(s, defaultValue) {\n if (typeof(s) === 'undefined') {\n return defaultValue;\n }\n else if (typeof(s) === 'boolean') {\n return s;\n }\n else if (s == 'true') {\n return true;\n }\n else if (s == 'false') {\n return false;\n }\n else {\n return defaultValue;\n }\n};\n\nvar buildGridControlScope = function(scope, attrs) {\n /*if (attrs.type) {\n attrs = angular.extend({}, this.standardToolbarTypes[attrs.type] || {}, attrs);\n }*/\n\n scope.label = attrs.label;\n scope.href = attrs.href;\n scope.title = attrs.title;\n scope.buttonClass = attrs.class;\n scope.iconClass = attrs.icon;\n\n if (attrs.selectionAction) {\n scope.action = function() {\n var actionFunction = scope.$eval(attrs.selectionAction);\n if (scope.isEnabled && actionFunction) {\n actionFunction(scope.getSelection());\n }\n };\n }\n else if (attrs.action) {\n scope.action = function() {\n var actionFunction = scope.$eval(attrs.action);\n if (scope.isEnabled && actionFunction) {\n actionFunction();\n }\n };\n }\n\n var lengthConstraint, valueConstraint;\n if (attrs.selectionRange) {\n switch (attrs.selectionRange) {\n case '0': \n lengthConstraint = function(sel) { return sel.length == 0 };\n break;\n case '0+': \n lengthConstraint = function(sel) { return true };\n break;\n case '1':\n lengthConstraint = function(sel) { return sel.length == 1 };\n break;\n case '1+':\n lengthConstraint = function(sel) { return sel.length >= 1 };\n break;\n case '2+':\n lengthConstraint = function(sel) { return sel.length >= 2 };\n break;\n default:\n throw \"grid-selection-control selection-range not valid\";\n }\n }\n\n if (attrs.selectionConstraint) {\n valueConstraint = function(sel) {\n for (var i=0; i
    \",\n compile: function(tElem,tAttrs,transcludeFn) {\n return {\n pre: function(scope,elem,attrs) {\n var multiSelect = parseBooleanOption(attrs.multiSelect, true);\n var ctrlMultiSelect = parseBooleanOption(attrs.multiSelectWithCtrl, true);\n var selectCheckbox = multiSelect && !ctrlMultiSelect;\n\n scope.grid = {\n data: scope.$eval(attrs.data),\n columnDefs: [],\n\n // Filtering is awful.\n enableFiltering: parseBooleanOption(attrs.enableFiltering, false),\n enableSorting: parseBooleanOption(attrs.enableSorting, true),\n flatEntityAccess: true,\n enableColumnResizing: parseBooleanOption(attrs.enableColumnResizing, true),\n enablePagination: parseBooleanOption(attrs.enablePagination, true),\n enablePaginationControls: parseBooleanOption(attrs.enablePagination, true),\n paginationPageSizes: [15, 25, 50, 100, 500, 1000, 5000],\n paginationPageSize: attrs.pageSize ? 1*attrs.pageSize : 1000,\n enableColumnMenus: parseBooleanOption(attrs.enableColumnMenus, true),\n\n\n enableRowSelection: true,\n enableRowHeaderSelection: selectCheckbox,\n showGridFooter: parseBooleanOption(attrs.showGridFooter, true),\n enableFooterTotalSelected: true,\n multiSelect: multiSelect,\n modifierKeysToMultiSelect: ctrlMultiSelect,\n\n enableGridMenu: true,\n\n //footerRowHeight: 45,\n //headerRowHeight: 25,\n //rowHeight: 20,\n //rowEditHeight: attrs.rowEditHeight || 24,\n //rowEditPanelHeight: 50,\n //showSelectionCheckbox: parseBooleanOption(attrs.showSelectionCheckbox, false),\n style: attrs.style,\n\n onRegisterApi: function(gridApi) {\n scope.gridApi = gridApi;\n }\n };\n\n scope.getSelection = function() {\n return scope.gridApi ? scope.gridApi.selection.getSelectedRows() : [];\n };\n }\n }\n }\n }\n})\n\n.directive('gridTemplate', function() {\n return {\n scope: false,\n restrict: 'E',\n //priority: 1900,\n compile: function(tElem, tAttrs) {\n return {\n pre: function(scope, elem, attrs) {\n var type = camelCase(attrs.type);\n var template = elem[0].innerHTML;\n template = template.replace(/{ {/g,'{{');\n template = template.replace(/} }/g,'}}');\n template = template.replace(/\\s*\\n\\s*/g,'');\n template = template.replace(/.*.*/,'>');\n\n scope.grid[type] = template;\n elem.innerHtml = '';\n //tmpl.remove();\n }\n }\n }\n }\n})\n\n\n.directive('gridColumnTemplate', function() {\n return {\n scope: false,\n restrict: 'E',\n //priority: 1900,\n compile: function(tElem, tAttrs) {\n return {\n pre: function(scope, elem, attrs) {\n var type = camelCase(attrs.type);\n var template = elem[0].innerHTML;\n template = template.replace(/{ {/g,'{{');\n template = template.replace(/} }/g,'}}');\n template = template.replace(/\\s*\\n\\s*/g,'');\n template = template.replace(/.*.*/,'>');\n\n scope.grid.columnDefs[scope.grid.columnDefs.length - 1][type] = template;\n elem.innerHtml = '';\n //tmpl.remove();\n }\n }\n }\n }\n})\n\n.directive('gridColumn', function() {\n return {\n scope: false,\n restrict: 'E',\n //priority: 1900,\n compile: function(tElem, tAttrs) {\n return {\n pre: function(scope, elem, attrs, transcludeFn) {\n if (typeof(scope.grid.columnDefs) == 'undefined') {\n scope.grid.columnDefs = [];\n }\n \n var column = {\n field: attrs.field,\n sortable: (attrs.sortable === 'false' ? false : true),\n displayName: attrs.displayName,\n cellFilter: attrs.cellFilter,\n width: attrs.width\n // Todo other fields\n };\n var doAdd = true;\n if (attrs.gcIf) {\n doAdd = scope.$eval(attrs.gcIf);\n }\n if (doAdd) {\n scope.grid.columnDefs.push(column);\n if (typeof(scope.grid.sortInfo) === 'undefined') {\n scope.grid.sortInfo = {fields:[], directions:[]};\n }\n\n if ((attrs.sort === 'asc') || (attrs.sort === 'desc')) {\n scope.grid.sortInfo.fields.push(attrs.field);\n scope.grid.sortInfo.directions.push(attrs.sort);\n //Log.ger.dir(scope.grid.sortInfo);\n }\n }\n }\n }\n }\n }\n})\n\n\n.directive('toolButton', function() {\n return {\n restrict: 'E',\n transclude: true,\n replace: false,\n template: '',\n scope: true,\n link: function(scope, elem, attrs) {\n buildGridControlScope(scope, attrs);\n }\n }\n})\n\n.directive('toolMenuButton', function() {\n return {\n restrict: 'E',\n transclude: true,\n template: '
    ' +\n '' +\n '
      ' +\n '
      ',\n scope: true,\n link: function(scope, elem, attrs) {\n buildGridControlScope(scope, attrs);\n }\n }\n})\n\n.directive('toolMenuEntry', function() {\n return {\n restrict: 'E',\n transclude: true,\n template: '
    • ',\n scope: true,\n replace: true,\n link: function(scope, elem, attrs) {\n buildGridControlScope(scope, attrs);\n }\n }\n})\n\n\n\n\n;\n\n})();\n","(function () {\n'use strict';\n\nangular.module('kohaacqlists', ['ngResource'])\n.controller('UserAcqListsCtrl', [\"$scope\", \"acqListsModel\", \"acqListControlsAPI\", \"resolvedAcqLists\", \"userService\", \"$uibModal\", \"$state\", \"loading\", \"alertService\", function($scope, acqListsModel, acqListControlsAPI, resolvedAcqLists,\n userService, $uibModal, $state, loading, alertService) {\n\n $scope.user = userService;\n $scope.lists = resolvedAcqLists;\n\n $scope.$on('sys.signal.ItemListHoldings', function(evnt, args) { \n // TODO this doesn't actually do anything yet\n angular.forEach($scope.lists, function(elem) {\n if (elem.id == args.item_list_id) {\n $scope.$class = 'row-success';\n }\n });\n });\n\n $scope.reload = function() {\n loading.add();\n acqListsModel.forUser({user: userService.id}).$promise.then(function(rv) {\n loading.resolve();\n $scope.lists.replaceWith(rv);\n }, function() {\n loading.resolve();\n });\n };\n\n $scope.details = function(entries) {\n $state.go('me.acqlists-details', {id: entries[0].id});\n };\n\n $scope.create = function() {\n acqListControlsAPI.createList().then($scope.reload);\n };\n\n $scope.update = function(entries) {\n acqListControlsAPI.editList(entries[0]).then($scope.reload);\n };\n\n $scope.copy = function(sels) {\n acqListControlsAPI.copyList(sels[0]).then($scope.reload);\n };\n\n $scope.delete = function(sels) {\n return acqListControlsAPI.deleteLists(sels).then($scope.reload);\n };\n\n $scope.purge = function(sels) {\n return acqListControlsAPI.purgeLists(sels).then($scope.reload);\n };\n \n $scope.cancel = function(sels){\n return acqListControlsAPI.cancelLists(sels).then($scope.reload);\n }\n \n $scope.remind = function(sels){\n return acqListControlsAPI.remindLists(sels).then($scope.reload);\n }\n \n $scope.renew = function(sels){\n var needsCheck = false;\n var ids = [];\n angular.forEach(sels, function(sel) { ids.push(sel.id); });\n acqListsModel.getAllEntries({ids: ids}).$promise.then(function(rv) {\n $uibModal.open({\n templateUrl: '/app/static/partials/user/acqlists/renew-selected.html',\n controller: 'UserAcqListsRenewCtrl',\n backdrop: false,\n size: 'lg',\n backdropClick: false,\n resolve: {\n datum: function() { return {listitems : sels, hash : rv.hash }; },\n needsCheck: function() { return needsCheck; },\n renewLists: function() {\n return function(obj) {\n loading.add();\n var ids = [];\n var names = [];\n \n angular.forEach(obj.entries, function(entry) { \n \n if(entry.item && entry.renewable){\n ids.push(entry.id);\n names.push(entry.name);\n }\n\n });\n var payload = {ids: ids, names : names, notify : obj.notify};\n acqListsModel.renewAll(payload).$promise.then(function() {\n loading.resolve();\n $scope.reload();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Merge failed');\n $scope.reload();\n });\n }\n }\n }\n });\n }, function() {\n loading.resolve();\n });\n \n\n }\n \n $scope.merge = function(sels){\n var needsCheck = false;\n $uibModal.open({\n templateUrl: '/app/static/partials/user/acqlists/merge-selected.html',\n controller: 'UserAcqListsMergeCtrl',\n backdrop: false,\n size: 'lg',\n backdropClick: false,\n resolve: {\n entries: function() { return sels; },\n needsCheck: function() { return needsCheck; },\n mergeLists: function() {\n return function(obj) {\n loading.add();\n var ids = [];\n angular.forEach(sels, function(sel) { ids.push(sel.id); });\n acqListsModel.mergeAll({ids: ids, purge : obj.purge ? 1 : null, default_id : obj.radioModel == \"New\" ? null : obj.default_id, name : obj.radioModel == \"Existing\" ? null :obj.name}).$promise.then(function() {\n loading.resolve();\n $scope.reload();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Merge failed');\n $scope.reload();\n });\n }\n }\n }\n });\n }\n\n $scope.share = function(sels) {\n return acqListControlsAPI.shareList(sels[0]).then($scope.reload);\n };\n\n $scope.holdings = function(sels) {\n return acqListControlsAPI.getListHoldings(sels[0]);\n };\n\n $scope.discounts = function(sels) {\n return acqListControlsAPI.getListDiscounts(sels[0]);\n };\n\n $scope.checkout = function(sels) {\n return acqListControlsAPI.checkout(sels[0]).then($scope.reload);\n };\n\n $scope.setStatus = function(sels) {\n return acqListControlsAPI.setStatus(sels);\n };\n\n $scope.export = function(exportMethod) {\n return function(sels) {\n return acqListControlsAPI.export(sels, exportMethod);\n };\n };\n\n $scope.acqlists_simple_view_pref = userService.prefs.acqlists_simple_view;\n\n $scope.single_list_mode = function () {\n var filtered = {};\n filtered['acqlists_simple_view'] = true;\n\n userService.setAllPrefs(filtered)\n .then( function () {\n var list_id, openList, deferred_promise;\n\n while ( openList = $scope.lists.shift() ) { // eslint-disable-line no-cond-assign\n if ( openList.status === 'open' ) {\n break;\n }\n else {\n openList = undefined;\n }\n }\n\n if ( !openList ) {\n deferred_promise = acqListControlsAPI.createList();\n deferred_promise.then( function (list) {\n $state.go('me.acqlists-details', { id: list.id });\n }, function (reason) {\n console.error('New list creation failed: ' + reason);\n });\n }\n else {\n list_id = openList.id;\n $state.go('me.acqlists-details', { id: list_id });\n }\n }, function (error) {\n console.error(\"Preference not saved: acqlists_simple_view: \" + error);\n });\n }\n\n}])\n\n.controller('UserAcqListsButtonCtrl', [\"$scope\", \"acqListControlsAPI\", \"userService\", \"$state\", \"resolvedAcqLists\", function($scope, acqListControlsAPI, userService, $state, resolvedAcqLists) {\n $scope.lists = resolvedAcqLists;\n\n if ( !userService.prefs.acqlists_simple_view ) {\n $state.go('me.acqlists');\n }\n else {\n var list_id, openList;\n\n while ( openList = $scope.lists.shift() ) { // eslint-disable-line no-cond-assign\n if ( openList.status === 'open' ) {\n break;\n }\n else {\n openList = undefined;\n }\n }\n\n if ( !openList ) {\n var deferred_promise = acqListControlsAPI.createList();\n deferred_promise.then( function (e) {\n $state.go('me.acqlists-details', { id: e.id });\n }, function(reason) {\n console.error('New list creation failed: ' + reason);\n });\n }\n else {\n list_id = openList.id;\n $state.go('me.acqlists-details', { id: list_id });\n }\n }\n}])\n\n.controller('UserAcqListDetailsCtrl', [\"$scope\", \"$state\", \"$stateParams\", \"acqListsModel\", \"resolvedAcqList\", \"resolvedAcqListEntries\", \"userService\", \"loading\", \"alertService\", \"$uibModal\", \"acqListControlsAPI\", \"acqListUtilAPI\", function($scope, $state, $stateParams, acqListsModel, resolvedAcqList, resolvedAcqListEntries, userService, loading, alertService, $uibModal, acqListControlsAPI, acqListUtilAPI) {\n\n $scope.acqlists_simple_view_pref = userService.prefs.acqlists_simple_view;\n\n $scope.user = userService;\n $scope.list = resolvedAcqList;\n $scope.list.badSubscription = null;\n $scope.months = [ \"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"];\n \n $scope.loadCustomData = function(entry){\n if(!entry.sub_start_month || !entry.sub_start_year || !entry.sub_renewal_month || !entry.sub_end_year){\n $scope.list.badSubscription = true;\n }\n entry.subscriptionDate = (entry.sub_start_month && entry.sub_start_year) ? $scope.months[entry.sub_start_month-1] + \"/\" + entry.sub_start_year : \"\" ;\n entry.renewalMonth = entry.sub_renewal_month ? $scope.months[entry.sub_renewal_month-1] : \"\";\n }\n $scope.entries = resolvedAcqListEntries;\n if($scope.list.type == \"UPAK UPR\"){\n $scope.list.badSubscription = null;\n for(var i = 0; i < $scope.entries.length ; i++){\n var currEntry = $scope.entries[i];\n $scope.loadCustomData($scope.entries[i]);\n \n }\n }\n $scope.queryScope = 'all';\n $scope.$watch('queryScope', function(newVal, oldVal) {\n if (oldVal && (newVal !== oldVal)) {\n $scope.reload();\n }\n });\n\n\n $scope.reload = function() {\n loading.add();\n acqListsModel.entries({id: $stateParams.id, scope: $scope.queryScope}).$promise.then(function(rv) {\n loading.resolve();\n $scope.list.badSubscription = null;\n if($scope.list.type == \"UPAK UPR\"){\n for(var i = 0; i < rv.length; i++){\n $scope.loadCustomData(rv[i]);\n }\n }\n $scope.entries.replaceWith(rv);\n }, function() {\n loading.resolve();\n });\n };\n\n\n $scope.$on('sys.signal.ItemListHoldings', function(evnt, args) { \n if (args.item_list_id == $scope.list.id) {\n //console.log(\"Reloading\");\n $scope.reload();\n }\n });\n\n $scope.reloadList = function() {\n loading.add();\n acqListsModel.details({id: $stateParams.id}).$promise.then(function(rv) {\n loading.resolve();\n $scope.list.name = rv.name;\n $scope.list.type = rv.type;\n }, function() {\n loading.resolve();\n });\n };\n\n $scope.listUpdate = function() {\n acqListControlsAPI.editList($scope.list).then($scope.reloadList);\n };\n\n $scope.listCopy = function() {\n acqListControlsAPI.copyList($scope.list).then($scope.reloadList);\n };\n\n $scope.listShare = function() {\n return acqListControlsAPI.shareList($scope.list).then($scope.reloadList);\n };\n\n $scope.listHoldings = function() {\n return acqListControlsAPI.getListHoldings($scope.list);\n };\n\n $scope.listDiscounts = function() {\n return acqListControlsAPI.getListDiscounts($scope.list);\n };\n\n $scope.listCheckout = function() {\n return acqListControlsAPI.checkout($scope.list);\n };\n\n $scope.listExport = function(exportMethod) {\n return acqListControlsAPI.export([$scope.list], exportMethod);\n };\n\n $scope.delete = function(sels) {\n loading.add();\n var ids = [];\n angular.forEach(sels, function(sel) { ids.push(sel.id); });\n acqListsModel.deleteAllEntries({id: $scope.list.id},{ids: ids}).$promise.then(function() {\n loading.resolve();\n $scope.reload();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Delete failed');\n $scope.reload();\n });\n };\n \n \n \n $scope.subupdate = function(sels){\n $uibModal.open({\n templateUrl: '/app/static/partials/user/acqlists/update-subscriptions.html',\n controller: 'UserAcqListsUpdateSubscriptionsCtrl',\n backdrop: false,\n size: 'lg',\n backdropClick: false,\n resolve: {\n entries: function() { return sels; },\n updateSubscriptions: function() {\n return function(obj) {\n loading.add();\n var ids = [];\n angular.forEach(sels, function(sel) { ids.push(sel.id); });\n acqListsModel.updateAllEntries({id: $scope.list.id},{ids: ids, data : obj}).$promise.then(function() {\n loading.resolve();\n $scope.reload();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Update failed');\n $scope.reload();\n });\n }\n }\n }\n });\n }\n $scope.isSub = $scope.list.type == \"UPAK UPR\" ? true : false ; \n $scope.holdings = function(sels) {\n var needsCheck = false;\n angular.forEach(sels, function(sel) {\n if (sel.holdings_json) {\n sel.$holdings = acqListUtilAPI.transformHoldingsEntry(sel.holdings_json);\n }\n else {\n needsCheck = true;\n }\n });\n\n $uibModal.open({\n templateUrl: '/app/static/partials/user/acqlists/holdings-results.html',\n controller: 'UserAcqListsHoldingsCtrl',\n backdrop: false,\n size: 'lg',\n backdropClick: false,\n resolve: {\n entries: function() { return sels; },\n needsCheck: function() { return needsCheck; },\n updateRows: function() {\n return function(recs) {\n var update = {};\n angular.forEach(recs, function(r) {\n update[r.id] = r;\n });\n angular.forEach($scope.entries, function(r) {\n if (update[r.id]) {\n r.holdings_json = update[r.id]._embed.holdings_json;\n r.holdings_check_date = update[r.id]._embed.holdings_check_date;\n }\n });\n }\n }\n }\n });\n };\n\n $scope.multi_list_mode = function () {\n var filtered = {};\n filtered['acqlists_simple_view'] = false;\n\n userService.setAllPrefs(filtered)\n .then( function () {\n $state.go('me.acqlists');\n }, function (e) {\n console.error(\"Preference not saved: acqlists_simple_view: \" + e);\n });\n }\n}])\n\n.controller('UserAcqSubsCtrl', [\"$scope\", \"resolvedOrders\", \"configService\", function($scope, resolvedOrders, configService) {\n\n $scope.title = configService.AcqList.wording.activeSubs || 'current subscriptions';\n\n $scope.orders = resolvedOrders.map(function(o){\n var tags = angular.copy(o.order_tags);\n var order = tags.shift();\n order.tags_history = tags; // For now, just use most recent tag.\n return order;\n });\n\n}])\n\n.controller('UserAcqListsHoldingsCtrl', [\"$scope\", \"$uibModalInstance\", \"entries\", \"needsCheck\", \"acqListsModel\", \"acqListUtilAPI\", \"alertService\", \"updateRows\", function($scope, $uibModalInstance, entries, needsCheck, acqListsModel, acqListUtilAPI, alertService, updateRows) {\n $scope.entries = entries;\n $scope.close = function() {\n $uibModalInstance.close();\n };\n\n $scope.refresh = function(getAll) {\n //console.log(\"Refresh \" + getAll);\n var params = {\n async: false\n };\n params.ids = [];\n angular.forEach(entries, function(e) {\n if (getAll || !e.$holdings)\n params.ids.push(e.id);\n });\n\n $scope.loading = true;\n acqListsModel.checkHoldingsFor({id: entries[0].item_list_id}, params).$promise.then(function(recs) {\n var newHoldings = {};\n angular.forEach(recs, function(rec) {\n if (rec._embed.holdings_json)\n newHoldings[rec.id] = acqListUtilAPI.transformHoldingsEntry(rec._embed.holdings_json);\n });\n angular.forEach($scope.entries, function(e) {\n e.checking = false;\n e.$holdings = newHoldings[e.id];\n });\n $scope.loading = false;\n updateRows(recs);\n }, function(err) {\n alertService.addApiError(err,'Holdings check failed');\n $scope.loading = false;\n });\n };\n\n if (needsCheck) {\n //console.log(\"Needs refreshing\");\n $scope.refresh(false);\n }\n}])\n\n.controller('UserAcqListsUpdateSubscriptionsCtrl', [\"$scope\", \"$uibModalInstance\", \"entries\", \"updateSubscriptions\", function($scope, $uibModalInstance, entries, updateSubscriptions) {\n var months = [ \"January\", \"February\", \"March\", \"April\", \"May\", \"June\", \"July\", \"August\", \"September\", \"October\", \"November\", \"December\"];\n $scope.months = [];\n for (var i=0; i v.sub_end_year) {\n bad_dates++;\n }\n if (!(v.sub_start_year && v.sub_end_year && v.sub_start_month && v.sub_renewal_month)) {\n missing++;\n }\n }\n if (missing) $scope.errors.push(\"All dates must be filled in\");\n if (bad_dates) $scope.errors.push(\"Subscription end year must be after subscription start year\");\n }, true);\n\n $scope.cancel = function() {\n $uibModalInstance.close();\n };\n\n $scope.close = function() {\n updateSubscriptions($scope.entries[0]);\n $uibModalInstance.close();\n };\n \n\n //$scope.formdata = {startMonth :entry.sub_start_month , startYear :entry.sub_start_year , renewalMonth : entry.sub_renewal_month , endYear : entry.sub_end_year};\n $scope.formdata = {};\n \n $scope.calendar = {dateFormat: 'MM/dd/yyyy', opened : false};\n $scope.title = \"Update Subscription Information\"\n \n/* setTimeout(function () {\n $scope.$apply(function () {\n \n $scope.setVals(entries[0],true);\n });\n }, 500);*/\n \n}])\n.controller('UserAcqListsMergeCtrl', [\"$scope\", \"$uibModalInstance\", \"entries\", \"alertService\", \"mergeLists\", function($scope, $uibModalInstance, entries, alertService, mergeLists) {\n $scope.entries = entries;\n \n \n $scope.close = function(obj) {\n if(obj){\n \n if(obj.radioModel == \"New\" &&(obj.name == null || obj.name == \"\")){\n alertService.add({msg: \"New List Must Have a Name\", type: 'error'});\n return;\n }\n \n var result = true;\n if(obj.purge){\n result = window.confirm(\"Are You sure you want to permanently delete all the lists that do not have entries?\");\n }\n if(result)\n mergeLists(obj);\n else\n return;\n }\n $uibModalInstance.close();\n };\n \n $scope.formdata = {\n newName: '',\n default_id: entries[0].id, \n purge : false,\n radioModel : 'Existing',\n \n }\n \n \n \n \n}])\n.controller('UserAcqListsRenewCtrl', [\"$scope\", \"$uibModalInstance\", \"datum\", \"alertService\", \"renewLists\", function($scope, $uibModalInstance, datum, alertService, renewLists) {\n $scope.entries = [];\n $scope.months = [ \"January\", \"February\", \"March\", \"April\", \"May\", \"June\", \"July\", \"August\", \"September\", \"October\", \"November\", \"December\"];\n var listHash = {};\n var index = 1;\n for(var i = 0; i < datum.listitems.length; i++){\n var listItem = datum.listitems[i];\n var entryListItem = {item : true, \"class\" : \"active\", renewable : (listItem.type == \"SUB SUB\" || listItem.type == \"UPAK UPR\") , name : listItem.name, id: listItem.id, type : listItem.type, branch_code : listItem.branch_code, index : index};\n $scope.entries.push(entryListItem);\n var hashVals = datum.hash[listItem.id];\n if(listItem.type != \"SUB SUB\" && listItem.type != \"UPAK UPR\"){\n hashVals = []; \n $scope.entries.push({item : false, \"class\" : \"danger\" , renewed : false, calculation : \"Only Subscription Item Lists can Be renewed\", title : \"Not A Subscription Item List\", name : \"\", id: 0})\n }\n var hasRenew = false;\n for(var x = 0; x < hashVals.length; x++){\n var entry = hashVals[x];\n var calc = \"\";\n var renewed = false;\n var start_year = entry.sub_start_year;\n var start_month = entry.sub_start_month;\n var end_year = entry.sub_end_year;\n var renewal_month = entry.sub_renewal_month;\n \n if(start_year && start_month && end_year && renewal_month){\n if((start_year + 1) < end_year){\n var month = $scope.months[renewal_month];\n calc = \"New renewal date will be \" + month + \"/\" + ((start_year * 1) + 1);\n renewed = true;\n hasRenew = true;\n entryListItem.renew = true;\n }else{\n calc = \"Renewal would put it passed the ending year\"\n }\n }else{\n calc = \"Can't Renew - Missing subscription field(s)\"\n }\n $scope.entries.push({item : false, \"class\" : renewed ? \"success\" : \"danger\" , renewed : renewed, calculation : calc, title : entry.title, name : entry.name, id: entry.id})\n }\n \n entryListItem.renewable = hasRenew;\n index++;\n }\n \n \n $scope.close = function(obj) {\n if(obj){\n \n var oneRenewable = false;\n var oneBadName = false;\n for(var i = 0; i < obj.entries.length; i++){\n if(obj.entries[i].item && (obj.entries[i].renewable))\n oneRenewable = true;\n if(obj.entries[i].item && (obj.entries[i].name == null || obj.entries[i].name == \"\")){\n oneBadName = true;\n }\n }\n if(!oneRenewable){\n alertService.add({msg: \"Merge failed: No Renewable Item Lists Selected\", type : \"error\"});\n $uibModalInstance.close();\n return; \n }\n else{\n if(oneBadName){\n alertService.add({msg: \"Merge failed: Name cannot be empty\", type : \"error\"});\n return;\n }\n renewLists(obj);\n \n }\n }\n $uibModalInstance.close();\n };\n \n $scope.formdata = {\n notify : false, \n entries : $scope.entries\n }\n \n \n \n \n}])\n\n\n\n\n\n/*\n $scope.canPurge = function() {\n return $scope.userdetails &&\n $scope.userdetails.permit.staffaccess &&\n $scope.userdetails.permit.staffaccess.all;\n };\n\n $scope.pbStates = [\n {icon: '', display: '', value: 0},\n {icon: 'icon-minus', display: '', value: 1},\n {icon: 'icon-plus', display: '', value: 2},\n ];\n\n\n $scope.actions = angular.extend({}, $scope.actions, {\n status: function(model, sels) {\n form.open({\n title: 'List Disposition',\n templateUrl: '/app/static/partials/ebs/status.html',\n formdata: {\n requestLists: sels,\n listStatus: sels[0].status\n }\n }).then(function(formdata) {\n angular.forEach(formdata.requestLists, function(sel) {\n loading.add();\n sel.status = formdata.listStatus;\n sel.setStatus(undefined, {\n comments: formdata.comments,\n }).then(function(resp) {\n loading.resolve();\n alertService.add({msg: resp.content});\n }, function() {\n loading.resolve();\n });\n });\n });\n },\n\n\n export: function(how) {\n return function(model, sels) {\n var ids = [];\n angular.forEach(sels, function(sel) {\n ids.push(sel.id);\n });\n\n form.open({\n title: 'Export (' + how + ')',\n templateUrl: '/app/static/partials/ebs/export.html',\n header: '',\n footer: '',\n formdata: {\n download: (how == 'download' ? true : false),\n format: 'xlsx',\n id: ids[0],\n ids: ids.join(','),\n email: $scope.userdetails.email,\n }\n }).then(function(formdata) {\n // note - promise can only be resolved if we're emailing\n return sels[0].export({\n ids: ids.join(','),\n email: formdata.email,\n format: formdata.format,\n view: 'download'\n });\n }).then(function(resp) {\n alertService.add({msg: \"Email sent\"});\n });\n };\n },\n\n commitment: function(model, sels) {\n var sel = sels[0];\n sel.calculateDiscounts().then(function(resp) {\n return form.open({\n title: 'Check Out and Commit',\n templateUrl: '/app/static/partials/ebs/commitment.html',\n header: '',\n footer: '',\n formdata: { \n vendors: resp,\n email: configService.CommitmentFormAddress,\n cc: $scope.userdetails.email\n }\n });\n }).then(function(formdata) {\n loading.add();\n return sel.sendCommitmentForm(undefined,{\n email: formdata.email,\n cc: formdata.cc,\n format: 'xlsx',\n view: 'download',\n check_holdings: formdata.checkHoldings,\n drop_held: formdata.dropHeld,\n ids: sel.id\n });\n }).then(function(resp) {\n loading.resolve();\n alertService.add({msg: \"Commitment Form request processed\"});\n }, function() {\n loading.resolve();\n });\n },\n\n discounts: function(model, sels) {\n loading.add();\n sels[0].calculateDiscounts().then(function(resp) {\n loading.resolve();\n form.open({\n title: 'Discounts',\n templateUrl: '/app/static/partials/ebs/list-discounts.html',\n header: '',\n footer: '',\n formdata: {vendors: resp}\n });\n }, function() {\n loading.resolve();\n });\n },\n\n\n },\n });*/\n\n\n// Service to encapsulate list functions injected into multiple controllers\n.factory('acqListControlsAPI', [\"acqListsModel\", \"kwApi\", \"$q\", \"modalForm\", \"loading\", \"configService\", \"alertService\", \"userService\", \"messageBox\", function(acqListsModel, kwApi, $q, modalForm, loading, configService, alertService, userService, messageBox) {\n var api = {};\n\n // View API functions return a promise which is resolved if reload is required\n // rejected otherwise (unless otherwise specified)\n \n api.createList = function() {\n var deferred = $q.defer();\n modalForm.open({\n title: 'New ' + configService.AcqList.wording.title,\n templateUrl: '/app/static/partials/user/acqlists/update.html',\n formdata: {branch_code: userService.login_branch, type: 'OTAC PnM'},\n header: '',\n footer: '',\n }).then(function(data) {\n loading.add();\n acqListsModel.save(data).$promise.then(function(e) {\n loading.resolve();\n //alertService.add({msg: 'List saved'});\n deferred.resolve(e);\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Save failed');\n deferred.reject();\n });\n }, function() {\n deferred.reject();\n });\n return deferred.promise;\n };\n\n api.editList = function(list) {\n var deferred = $q.defer();\n modalForm.open({\n title: 'Edit ' + configService.AcqList.wording.title,\n templateUrl: '/app/static/partials/user/acqlists/update.html',\n formdata: list,\n header: '',\n footer: '',\n }).then(function(list) {\n loading.add();\n acqListsModel.update(list).$promise.then(function() {\n loading.resolve();\n deferred.resolve();\n }, function(err) {\n alertService.addApiError(err,'Save failed');\n loading.resolve();\n deferred.resolve();\n });\n }, function() {\n deferred.resolve();\n });\n return deferred.promise;\n };\n\n api.copyList = function(list) {\n var deferred = $q.defer();\n modalForm.open({\n title: 'Copy ' + configService.AcqList.wording.title,\n templateUrl: '/app/static/partials/user/acqlists/copy.html',\n formdata: {\n //name: 'Copy of ' + list.name,\n name: '',\n branch_code: userService.login_branch,\n type: list.type,\n }\n }).then(function(formdata) {\n loading.add();\n formdata.id = list.id;\n acqListsModel.clone(formdata,{}).$promise.then(function() {\n loading.resolve();\n deferred.resolve();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Save failed');\n deferred.reject();\n });\n }, function() {\n deferred.reject();\n });\n return deferred.promise;\n };\n\n api.deleteLists = function(sels) {\n var deferred = $q.defer();\n messageBox.confirm({\n title: \"Are you sure?\",\n message: \"Are you sure you want to delete \" + (sels.length>1 ? 'these lists' : 'this list') + '?'\n }).then(function(result) {\n loading.add();\n var ids = [];\n angular.forEach(sels, function(sel) { ids.push(sel.id); });\n acqListsModel.deleteAll({ids: ids}).$promise.then(function() {\n loading.resolve();\n deferred.resolve();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Delete failed');\n deferred.resolve();\n });\n }, function() {\n deferred.reject();\n });\n return deferred.promise;\n };\n\n api.purgeLists = function(sels) {\n var deferred = $q.defer();\n messageBox.confirm({\n title: \"Are you sure?\",\n message: \"Are you sure you want to purge \" + (sels.length>1 ? 'these lists' : 'this list') + '? This CANNOT be undone'\n }).then(function(result) {\n loading.add();\n var ids = [];\n angular.forEach(sels, function(sel) { ids.push(sel.id); });\n acqListsModel.purgeAll({ids: ids}).$promise.then(function() {\n loading.resolve();\n deferred.resolve();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Purge failed');\n deferred.resolve();\n });\n }, function() {\n deferred.reject();\n });\n return deferred.promise;\n };\n \n \n api.remindLists = function(sels) {\n var deferred = $q.defer();\n messageBox.confirm({\n title: \"Are you sure?\",\n message: \"Are you sure you want to send reminds \" + (sels.length>1 ? 'the owners of these lists' : 'the owner of this list') + '? '\n }).then(function(result) {\n loading.add();\n var ids = [];\n angular.forEach(sels, function(sel) { ids.push(sel.id); });\n acqListsModel.remindAll({ids: ids}).$promise.then(function() {\n loading.resolve();\n deferred.resolve();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Reminder failed');\n deferred.resolve();\n });\n }, function() {\n deferred.reject();\n });\n return deferred.promise;\n };\n\n api.setStatus = function(sels) {\n var deferred = $q.defer();\n var oldStatus = undefined;\n angular.forEach(sels, function(sel) {\n if (typeof(oldStatus) == 'undefined') {\n oldStatus = sel.status;\n }\n else if (oldStatus !== sels.status) {\n oldStatus = null;\n }\n });\n //console.log(\"Status = \" + oldStatus);\n\n modalForm.open({\n title: 'Set List Status',\n templateUrl: '/app/static/partials/user/acqlists/status.html',\n formdata: {\n status: oldStatus\n }\n }).then(function(formdata) {\n loading.add();\n var ids = [];\n angular.forEach(sels, function(sel) { ids.push(sel.id); });\n acqListsModel.setStatusAll({ids: ids, status: formdata.status, comments: formdata.comments}).$promise.then(function() {\n angular.forEach(sels, function(sel) { sel.status = formdata.status; });\n loading.resolve();\n deferred.resolve();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Status change failed');\n deferred.resolve();\n });\n }, function() {\n deferred.reject();\n });\n return deferred.promise;\n };\n \n api.cancelLists = function(sels) {\n var deferred = $q.defer();\n messageBox.confirm({\n title: \"Are you sure?\",\n message: \"Are you sure you want to cancel \" + (sels.length>1 ? 'these lists' : 'this list') + '?'\n }).then(function(result) {\n loading.add();\n var ids = [];\n angular.forEach(sels, function(sel) { ids.push(sel.id); });\n acqListsModel.setStatusAll({ids: ids, status: 'cancelled'}).$promise.then(function() {\n loading.resolve();\n deferred.resolve();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Cancellation failed');\n deferred.resolve();\n });\n }, function() {\n deferred.reject();\n });\n return deferred.promise;\n \n };\n\n\n\n api.shareList = function(rlist) {\n var deferred = $q.defer();\n loading.add();\n $q.all([\n kwApi.Patron.getBranchList({permit:'editcatalogue.item_list.update'}).$promise,\n acqListsModel.accessors({id: rlist.id})\n ]).then(function(rv) {\n var candidates = rv[0];\n var selectors = rv[1];\n loading.resolve();\n\n var has = {};\n if (selectors) {\n angular.forEach(selectors, function(s) {\n has[s.member_id] = s;\n });\n }\n\n angular.forEach(candidates, function(candidate) {\n if (!has[candidate.id]) {\n candidate.enabled = 0;\n }\n else if (has[candidate.id].can_update == 0) {\n //console.log(\"Can update = 0\");\n candidate.enabled = 1;\n candidate.accessor_id = has[candidate.id].id;\n }\n else {\n //console.log(\"Can update = 1\");\n candidate.enabled = 2;\n candidate.accessor_id = has[candidate.id].id;\n }\n candidate.previous = candidate.enabled;\n });\n\n return modalForm.open({\n title: 'Share ' + configService.AcqList.wording.title,\n templateUrl: '/app/static/partials/user/acqlists/sharing.html',\n formdata: {selectors: candidates, requestList: rlist},\n header: '',\n footer: '',\n });\n }).then(function(formdata) {\n var shared = {};\n angular.forEach(formdata.selectors, function(s) {\n if (s.enabled == 2) {\n shared[s.id] = 1;\n }\n else if (s.enabled == 1) {\n shared[s.id] = 0;\n }\n });\n loading.add();\n acqListsModel.shareWith({id: rlist.id}, {list_accessors: shared}).$promise.then(function() {\n loading.resolve();\n //alertService.add({msg: \"List sharing updated\"});\n deferred.resolve();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Update failed');\n deferred.resolve();\n });\n }, function() {\n deferred.reject();\n });\n\n return deferred.promise;\n };\n\n api.getListHoldings = function(list) {\n modalForm.open({\n title: 'Holdings Check',\n templateUrl: '/app/static/partials/user/acqlists/holdings.html',\n header: '',\n footer: '',\n formdata: {}\n }).then(function(formdata) {\n acqListsModel.checkHoldings({id: list.id}, formdata).$promise.then(function() {\n alertService.add({msg: \"Holdings check started\"});\n }, function(err) {\n alertService.addApiError(err,'Holdings check failed');\n });\n });\n };\n\n api.getListDiscounts = function(list) {\n loading.add();\n acqListsModel.discounts({id: list.id}).$promise.then(function(rv) {\n //console.dir(rv);\n loading.resolve();\n modalForm.open({\n title: 'Volume Discounts',\n templateUrl: '/app/static/partials/user/acqlists/discounts.html',\n header: '',\n footer: '',\n formdata: rv,\n size: 'lg'\n });\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Discount calculation failed');\n });\n };\n\n api.checkout = function(list) {\n loading.add();\n return acqListsModel.discounts({id: list.id}).$promise.then(function(rv) {\n //console.dir(rv);\n loading.resolve();\n \n var workform = {}\n var formfields = [];\n var json = configService.AcqListsCommitmentFormFields;\n if (json) { \n angular.forEach(json, function(value) {\n formfields.push(value);\n });\n workform.formfields = formfields;\n }\n \n return modalForm.open({\n title: 'Checkout',\n templateUrl: '/app/static/partials/user/acqlists/checkout.html',\n header: '',\n footer: '',\n formdata: {list: rv, work_form: workform},\n size: 'lg'\n }).then(function(formdata) {\n formdata.work_form = angular.toJson(formdata.work_form); \n delete formdata.list;\n return acqListsModel.commit({id: list.id}, formdata).$promise.then(function(rv) {\n\n// RELOAD LIST SUMMARY .\n\n loading.resolve();\n alertService.add({msg: \"List committed\"});\n return true;\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Commit failed');\n });\n });\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Discount calculation failed');\n });\n };\n\n api.export = function(sels, how) {\n var list = sels[0];\n\n modalForm.open({\n title: 'Export (' + how + ')',\n templateUrl: '/app/static/partials/user/acqlists/export.html',\n header: '',\n footer: '',\n formdata: {\n id: list.id,\n download: (how == 'download' ? true : false),\n format: 'xlsx',\n email: userService.email,\n }\n }).then(function(formdata) { // Promise is only resolved if we're emailing\n loading.add();\n acqListsModel.export({id: list.id},formdata).$promise.then(function() {\n loading.resolve();\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Export failed');\n });\n });\n };\n\n return api;\n}])\n\n.factory('acqListSearchAPI', [\"acqListsModel\", \"userService\", \"bibService\", \"loading\", \"$q\", \"modalForm\", \"alertService\", \"kohaSearchSvc\", \"$http\", \"kwApi\", \"acqListsCartSvc\", function(acqListsModel, userService, bibService, loading, $q, modalForm, alertService, kohaSearchSvc, $http, kwApi, acqListsCartSvc) {\n var api = {};\n\n api.getReturnUrl = function(scope) {\n if (!scope.search)\n return window.location.origin + '/';\n var terms = [];\n var return_url = window.location.origin + scope.search.asUrl() + '#kohabib-' + scope.bib.id;\n console.log(return_url);\n return return_url;\n };\n\n\n api.addItemsToList = function(itemids, type, scope) {\n if (itemids.length !== 1) return;\n loading.add();\n var return_url = api.getReturnUrl(scope);\n acqListsModel.addToPortalCart({id: itemids[0], return_url: return_url}).$promise.then(function(rv) {\n loading.resolve();\n if (rv.redirect) {\n window.location.href = rv.redirect;\n }\n else if (rv.error) {\n alertService.add({msg: rv.error, type: 'error'});\n }\n else {\n alertService.add({msg: \"Product added to cart\"});\n acqListsCartSvc.updateCartCounts();\n }\n }, function(err) {\n loading.resolve();\n var s = (typeof(err) === 'object' ? err.statusText : err);\n alertService.add({msg: \"Could not add to cart: \" + s, type: 'error'});\n });\n };\n\n api.addItemsToListOld = function(itemids, bibids, types) {\n var typeList = Object.keys(types);\n var formdata = {\n itemids: itemids,\n bibs: [],\n id: (userService.prefs.currentAcqListId || ''),\n limitedTo: false,\n totalHits: false,\n types: typeList,\n type: typeList[0],\n typeMap: {\n 'OTAC PnM': 'One Time - Pick and Mix',\n 'OTAC DDA': 'One Time - Demand Driven Acq',\n 'SUB SUB': 'Subscription',\n 'UPAK UPOT': 'User Package - One Time',\n 'UPAK UPR': 'User Package - Recurring',\n },\n };\n \n angular.forEach(bibids, function(bibid) {\n loading.add();\n bibService.get(bibid).then(function(bib) {\n loading.resolve();\n formdata.bibs.push(bib);\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Could not retrieve title information');\n });\n });\n\n loading.add();\n acqListsModel.forUser({user: userService.id, open: 1}).$promise.then(function(rv) {\n formdata.lists = [];\n var listFound = false;\n angular.forEach(rv, function(lst) {\n if (types && types[lst.type]) {\n formdata.lists.push(lst);\n if (lst.id === formdata.id)\n listFound = true;\n }\n });\n if (!listFound)\n formdata.id = '';\n\n formdata.acqlists_simple_view_pref = userService.prefs.acqlists_simple_view;\n\n if (userService.prefs.acqlists_simple_view) {\n var list_id, openList;\n\n while ( openList = rv.shift() ) { // eslint-disable-line no-cond-assign\n if ( openList.status === 'open' ) {\n break;\n }\n else {\n openList = undefined;\n }\n }\n\n if ( !openList ) {\n formdata.name = undefined;\n var newTypes = {'OTAC PnM':1, 'OTAC DDA':1, 'SUB SUB':1, 'UPAK UPOT':1, 'UPAK UPR':1};\n formdata.types = Object.keys(newTypes);\n formdata.type = formdata.types[0];\n }\n else {\n formdata.id = openList.id;\n formdata.name = openList.name;\n }\n }\n\n loading.resolve();\n return modalForm.open({\n templateUrl: '/app/static/partials/user/acqlists/add-selected.html',\n header: '',\n footer: '',\n formdata: formdata,\n });\n }).then(function(formdata) {\n var deferred = $q.defer();\n\n if (formdata.id) {\n deferred.resolve(1);\n }\n else {\n loading.add();\n acqListsModel.save({name: formdata.name, type: formdata.type}).$promise.then(function(rv) {\n formdata.id = rv.id;\n loading.resolve();\n deferred.resolve();\n }, function(err) {\n loading.resolve();\n deferred.reject(err);\n })\n }\n\n deferred.promise.then(function() {\n userService.addPrefs({currentAcqListId: formdata.id});\n loading.add();\n return acqListsModel.append({id: formdata.id}, {items: itemids}).$promise;\n }).then(function(rv) {\n var stats = rv.content;\n var s = 'Added ' + stats.added + ' items to list (click to view)';\n if (stats.duplicate && stats.bad_type)\n s = s + ' (skipped ' + stats.duplicate + ' duplicates and ' + stats.bad_type + ' items which did not match list type)';\n else if (stats.duplicate)\n s = s + ' (skipped ' + stats.duplicate + ' duplicates)';\n else if (stats.bad_type) \n s = s + ' (skipped ' + stats.bad_type + ' items which did not match list type)';\n\n loading.resolve();\n alertService.add({msg: s});\n }, function(err) {\n alertService.addApiError(err,'Could not add to list');\n loading.resolve();\n });\n });\n };\n\n\n api.addBibsToList = function(bibids, types) {\n var curSearch = kohaSearchSvc.currentSearch();\n var qp = (curSearch) ? curSearch.solrParams() : {};\n\n if (qp.params && qp.params.fq) {\n var program;\n var order_type;\n angular.forEach(qp.params.fq, function(facet) {\n console.dir(facet);\n var args = facet.match(/program:\\(\\\"(.+)\\\"\\)/);\n if (args && args.length > 1) {\n program = args[1];\n }\n args = facet.match(/order_type:\\(\\\"(.+)\\\"\\)/);\n if (args && args.length > 1) {\n order_type = args[1];\n }\n });\n angular.forEach(types, function(val, key) {\n var ts = key.split(' ');\n console.dir(ts);\n if (order_type && (order_type != ts[0])) {\n delete types[key];\n }\n else if (program && (program != ts[1])) {\n delete types[key];\n }\n });\n }\n\n var typeList = Object.keys(types);\n var formdata = {\n bibids: bibids,\n bibs: [],\n queryParams: qp.params,\n id: userService.prefs.currentAcqListId,\n limitedTo: false,\n totalHits: false,\n types: typeList,\n type: typeList[0],\n typeMap: {\n 'OTAC PnM': 'One Time - Pick and Mix',\n 'OTAC DDA': 'One Time - Demand Driven Acq',\n 'SUB SUB': 'Subscription',\n 'UPAK UPOT': 'User Package - One Time',\n 'UPAK UPR': 'User Package - Recurring',\n },\n };\n \n angular.forEach(bibids, function(bibid) {\n loading.add();\n bibService.get(bibid).then(function(bib) {\n loading.resolve();\n formdata.bibs.push(bib);\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Could not retrieve title information');\n });\n });\n\n loading.add();\n acqListsModel.forUser({user: userService.id, open: 1}).$promise.then(function(rv) {\n formdata.lists = [];\n var listFound = false;\n angular.forEach(rv, function(lst) {\n if (types && types[lst.type]) {\n formdata.lists.push(lst);\n if (lst.id === formdata.id)\n listFound = true;\n }\n });\n if (!listFound)\n formdata.id = '';\n\n formdata.acqlists_simple_view_pref = userService.prefs.acqlists_simple_view;\n\n if (userService.prefs.acqlists_simple_view) {\n var list_id, openList;\n\n while ( openList = rv.shift() ) { // eslint-disable-line no-cond-assign\n if ( openList.status === 'open' ) {\n break;\n }\n else {\n openList = undefined;\n }\n }\n\n if ( !openList ) {\n formdata.name = undefined;\n var newTypes = {'OTAC PnM':1, 'OTAC DDA':1, 'SUB SUB':1, 'UPAK UPOT':1, 'UPAK UPR':1};\n formdata.types = Object.keys(newTypes);\n formdata.type = formdata.types[0];\n }\n else {\n formdata.id = openList.id;\n formdata.name = openList.name;\n }\n }\n\n loading.resolve();\n return modalForm.open({\n templateUrl: '/app/static/partials/user/acqlists/add-selected.html',\n header: '',\n footer: '',\n formdata: formdata,\n });\n }).then(function(formdata) {\n var deferred = $q.defer();\n\n if (formdata.id) {\n deferred.resolve(1);\n }\n else {\n loading.add();\n acqListsModel.save({name: formdata.name, type: formdata.type}).$promise.then(function(rv) {\n formdata.id = rv.id;\n loading.resolve();\n deferred.resolve();\n }, function(err) {\n loading.resolve();\n deferred.reject(err);\n })\n }\n\n deferred.promise.then(function() {\n userService.addPrefs({currentAcqListId: formdata.id});\n loading.add();\n return acqListsModel.append({id: formdata.id}, {bibs: bibids, params: JSON.stringify(formdata.queryParams)}).$promise;\n }).then(function(rv) {\n var stats = rv.content;\n var s = 'Added ' + stats.added + ' items to list (click to view)';\n if (stats.duplicate && stats.bad_type)\n s = s + ' (skipped ' + stats.duplicate + ' duplicates and ' + stats.bad_type + ' items which did not match list type)';\n else if (stats.duplicate)\n s = s + ' (skipped ' + stats.duplicate + ' duplicates)';\n else if (stats.bad_type) \n s = s + ' (skipped ' + stats.bad_type + ' items which did not match list type)';\n\n loading.resolve();\n alertService.add({msg: s});\n }, function(err) {\n alertService.addApiError(err,'Could not add to list');\n loading.resolve();\n });\n });\n };\n\n api.exportSearch = function(formdata) {\n modalForm.open({\n templateUrl: '/app/static/partials/user/acqlists/export-search.html',\n header: '',\n footer: '',\n formdata: formdata,\n }).then(function(formdata) {\n loading.add();\n kwApi.Catalog.exportSearch(formdata).$promise.then(function(rv) {\n loading.resolve();\n if (rv.response_code == '302') {\n alertService.add({msg: \"This search has already been exported\"});\n }\n else {\n alertService.add({msg: \"Search exported\"});\n }\n }, function(err) {\n alertService.addApiError(err,'Could not add to list');\n loading.resolve();\n });\n });\n };\n\n\n api.addQueryHitsToList = function(query) {\n //console.log(query.q);\n //console.dir(kohaSearchSvc.query.solrParams()); //.queryGetParams());\n\n var types = {'OTAC PnM':1, 'OTAC DDA':1, 'SUB SUB':1, 'UPAK UPOT':1, 'UPAK UPR':1};\n // Filter against current query params\n var curSearch = kohaSearchSvc.currentSearch();\n var qp = (curSearch) ? curSearch.solrParams() : {};\n if (qp.params && qp.params.fq) {\n var program;\n var order_type;\n angular.forEach(qp.params.fq, function(facet) {\n var args = facet.match(/program:\\(\\\"(.+)\\\"\\)/);\n if (args && args.length > 1) {\n program = args[1];\n }\n args = facet.match(/order_type:\\(\\\"(.+)\\\"\\)/);\n if (args && args.length > 1) {\n order_type = args[1];\n }\n });\n angular.forEach(types, function(val, key) {\n var ts = key.split(' ');\n if (order_type && (order_type != ts[0])) {\n delete types[key];\n }\n else if (program && (program != ts[1])) {\n delete types[key];\n }\n });\n }\n\n var formdata = {\n query: query.q,\n queryParams: qp.params,\n bibs: [],\n types: [],\n type: '',\n typeMap: {\n 'OTAC PnM': 'One Time - Pick and Mix',\n 'OTAC DDA': 'One Time - Demand Driven Acq',\n 'SUB SUB': 'Subscription',\n 'UPAK UPOT': 'User Package - One Time',\n 'UPAK UPR': 'User Package - Recurring',\n },\n id: (userService.prefs.currentAcqListId || ''),\n };\n \n\n loading.add('aqhtl-1');\n var searchDeferred = $q.defer();\n $http.get(\"/api/opac/\"+encodeURIComponent(query.q), {params: qp.params}).then(function(rsp) {\n searchDeferred.resolve(rsp.data);\n }, function(err) {\n searchDeferred.reject(err);\n });\n\n searchDeferred.promise.then(function(data) {\n var program_values, order_type_values;\n angular.forEach(data.facets, function(facet) {\n if (facet.field == 'program') {\n program_values = {};\n angular.forEach(facet.values, function(v) {\n var args = v.value.match(/\\\"(.+)\\\"/);\n if (args && args[1]) {\n program_values[args[1]] = 1;\n }\n else {\n program_values[v.value] = 1;\n }\n });\n }\n if (facet.field == 'order_type') {\n order_type_values = {};\n angular.forEach(facet.values, function(v) {\n var args = v.value.match(/\\\"(.+)\\\"/);\n if (args && args[1]) {\n order_type_values[args[1]] = 1;\n }\n else {\n order_type_values[v.value] = 1;\n }\n });\n }\n });\n\n angular.forEach(types, function(val, key) {\n var ts = key.split(' ');\n if (order_type_values && !order_type_values[ts[0]]) {\n delete types[key];\n }\n if (program_values && !program_values[ts[1]]) {\n delete types[key];\n }\n });\n\n formdata.types = Object.keys(types);\n formdata.type = formdata.types[0];\n\n loading.resolve('aqhtl-1');\n formdata.totalHits = data.total_hits;\n formdata.limitedTo = data.hits.length;\n angular.forEach(data.hits, function(hit) {\n // FIXME - this is cheesy af\n var id = hit.work.replace(/^.+\\//,'');\n //console.dir(id);\n if (id) {\n loading.add();\n bibService.get(id).then(function(bib) {\n loading.resolve();\n formdata.bibs.push(bib);\n }, function(err) {\n loading.resolve();\n alertService.addApiError(err,'Could not retrieve title information');\n });\n }\n });\n\n loading.add('aqhtl-2');\n return acqListsModel.forUser({user: userService.id, open: 1}).$promise;\n }, function(err) {\n loading.resolve('aqhtl-1');\n alertService.addApiError(err,'Could not retrieve title information');\n return $q.reject();\n }).then(function(rv) {\n //console.dir(rv);\n loading.resolve('aqhtl-2');\n\n formdata.lists = [];\n var listFound = false;\n angular.forEach(rv, function(lst) {\n if (types && types[lst.type]) {\n formdata.lists.push(lst);\n if (lst.id === formdata.id)\n listFound = true;\n }\n });\n if (!listFound)\n formdata.id = '';\n\n formdata.acqlists_simple_view_pref = userService.prefs.acqlists_simple_view;\n\n if (userService.prefs.acqlists_simple_view) {\n var list_id, openList;\n\n while ( openList = rv.shift() ) { // eslint-disable-line no-cond-assign\n if ( openList.status === 'open' ) {\n break;\n }\n else {\n openList = undefined;\n }\n }\n\n if ( !openList ) {\n formdata.name = undefined;\n var newTypes = {'OTAC PnM':1, 'OTAC DDA':1, 'SUB SUB':1, 'UPAK UPOT':1, 'UPAK UPR':1};\n formdata.types = Object.keys(newTypes);\n formdata.type = formdata.types[0];\n }\n else {\n formdata.id = openList.id;\n formdata.name = openList.name;\n }\n }\n\n return modalForm.open({\n templateUrl: '/app/static/partials/user/acqlists/add-selected.html',\n header: '',\n footer: '',\n formdata: formdata,\n });\n }, function(err) {\n loading.resolve('aqhtl-2');\n alertService.addApiError(err,'Could not retrieve lists');\n return $q.reject();\n }).then(function(formdata) {\n var deferred = $q.defer();\n\n if (formdata.id) {\n deferred.resolve(1);\n }\n else {\n loading.add();\n acqListsModel.save({name: formdata.name, type: formdata.type}).$promise.then(function(rv) {\n formdata.id = rv.id;\n loading.resolve();\n deferred.resolve();\n }, function(err) {\n loading.resolve();\n deferred.reject(err);\n })\n }\n\n deferred.promise.then(function() {\n userService.addPrefs({currentAcqListId: formdata.id});\n loading.add();\n return acqListsModel.appendSearchResults({id: formdata.id}, {query: formdata.query, params: JSON.stringify(formdata.queryParams)}).$promise;\n }).then(function(rv) {\n var stats = rv.content;\n var s = 'Added ' + stats.added + ' items to list (click to view)';\n if (stats.duplicate && stats.bad_type)\n s = s + ' (skipped ' + stats.duplicate + ' duplicates and ' + stats.bad_type + ' items which did not match list type)';\n else if (stats.duplicate)\n s = s + ' (skipped ' + stats.duplicate + ' duplicates)';\n else if (stats.bad_type) \n s = s + ' (skipped ' + stats.bad_type + ' items which did not match list type)';\n\n loading.resolve();\n alertService.add({msg: s});\n }, function(err) {\n alertService.addApiError(err,'Could not add to list');\n loading.resolve();\n });\n });\n };\n\n return api;\n}])\n\n.factory('acqListUtilAPI', [\"acqListsModel\", function(acqListsModel) {\n var api = {};\n\n api.routesReady = acqListsModel.getPortalRoutes().$promise;\n api.routesReady.then(function(routes) {\n api.routes = routes;\n });\n\n api.transformHoldingsEntry = function(sel) {\n var holdings = {};\n var parsed;\n try {\n parsed = JSON.parse(sel);\n if (parsed.error)\n holdings.globalError = parsed.error;\n }\n catch (e) {\n //console.log(e);\n holdings.globalError = \"Corrupted holdings data: \";\n }\n if (holdings.globalError)\n return(holdings);\n\n var hits = [];\n var errors = [];\n var noneAt = [];\n var totalTargets = 0;\n \n angular.forEach(parsed, function(details, targetName) {\n totalTargets++;\n if (typeof(details) == 'object') {\n if (details.length > 0) {\n angular.forEach(details, function(rec) {\n hits.push(angular.extend({target: targetName}, rec));\n });\n }\n else {\n noneAt.push(targetName);\n }\n }\n else {\n errors.push({target: targetName, error: details});\n }\n });\n holdings.hits = (hits.length ? hits : false);\n holdings.errors = (errors.length ? errors : false);\n holdings.noneAt = (noneAt.length ? noneAt.join(', ') : false);\n return holdings;\n };\n return api;\n}])\n\n.filter('holdingsFilter', function() {\n return function(json) {\n if (!json)\n return json;\n var holdings = JSON.parse(json);\n if (holdings.error) {\n return 'ERR: ' + holdings.error;\n }\n var rv = [];\n angular.forEach(holdings, function(hits, target) {\n if (typeof(hits) == 'object') {\n if (hits.length > 0) {\n rv.push(\"\" + hits.length + \" found (\" + target + \")\");\n }\n }\n else {\n rv.push(\"query failed (\" + target + \")\");\n }\n });\n return rv.join('; ');\n };\n})\n\n.factory('acqListsCartSvc', [\"acqListsModel\", \"userService\", \"acqListUtilAPI\", \"configService\", function(acqListsModel, userService, acqListUtilAPI, configService) {\n var svc = {\n count: 0\n };\n\n svc.goToCart = function() {\n acqListUtilAPI.routesReady.then(function(p) {\n var redirectUrl = acqListUtilAPI.routes.cart_url + '?return_url=' + encodeURIComponent(window.location.href);\n window.location.href = redirectUrl;\n\n });\n };\n\n svc.updateCartCounts = function() {\n if (!configService.AcqLists) return;\n acqListsModel.getPortalCart().$promise.then(function(rv) {\n console.dir(rv);\n svc.count = 0;\n if (rv && rv.orders && rv.orders.entries) {\n rv.orders.entries.forEach(function(e) {\n svc.count += e.count_products;\n });\n }\n });\n svc.count = 0;\n };\n\n svc.getCount = function() {\n return svc.count ? ('(' + svc.count + ')') : '';\n };\n\n userService.whenAnyUserDetails().then(function() {\n svc.updateCartCounts();\n });\n\n return svc;\n}])\n\n.controller('acqListsCartCtrl', [\"$scope\", \"acqListsCartSvc\", \"configService\", function($scope, acqListsCartSvc, configService) {\n if (configService.AcqLists) {\n $scope.goToPortalCart = acqListsCartSvc.goToCart;\n $scope.getPortalCartCount = acqListsCartSvc.getCount;\n }\n else {\n $scope.goToPortalCart = function() { };\n $scope.getPortalCartCount = function() { };\n }\n}])\n\n// ================================= Data models ================================================\n\n// TODO - move to kwApi\n.factory('acqListsModel', [\"$resource\", function($resource) {\n return $resource('/api/item-list/:id', {}, {\n details: {\n method: 'GET',\n url: '/api/item-list/:id?view=details&scope=shared',\n },\n discounts: {\n method: 'GET',\n url: '/api/item-list/:id?view=discounts',\n },\n entries: {\n method: 'GET',\n url: '/api/item-list/:id/entries?view=details&scope=:scope',\n isArray: true,\n },\n forUser: {\n method: 'GET',\n url: '/api/patron/:user/item-lists?view=details',\n isArray: true,\n },\n accessors: {\n method: 'GET',\n url: '/api/item-list/:id/accessors',\n isArray: true,\n //params: {id: '@id'}\n },\n branchOrders: {\n method: 'GET',\n url: '/api/branch/:branch_code/order_tags',\n isArray: true\n },\n update: {\n method: 'PUT',\n params: {id: '@id'}\n },\n\n append: {\n method: 'POST',\n url: '/api/item-list/:id?op=append'\n },\n appendSearchResults: {\n method: 'POST',\n url: '/api/item-list/:id?op=append-search'\n },\n clone: {\n method: 'POST',\n url: '/api/item-list/:id?op=clone&branch_code=:branch_code&name=:name',\n //params: {id: '@id'}\n },\n shareWith: {\n method: 'POST',\n url: '/api/item-list/:id?op=share',\n //params: {id: '@id'}\n },\n getAllEntries: {\n method: 'POST',\n url: '/api/item-list/?op=get_all_entries'\n },\n deleteAll: {\n method: 'POST',\n url: '/api/item-list/?op=delete_all'\n },\n deleteAllEntries: {\n method: 'POST',\n url: '/api/item-list/:id/entries?op=delete_all'\n },\n updateAllEntries: {\n method: 'POST',\n url: '/api/item-list/:id/entries?op=update_all'\n },\n purgeAll: {\n method: 'POST',\n url: '/api/item-list/?op=purge_all'\n },\n mergeAll: {\n method: 'POST',\n url: '/api/item-list/?op=merge_all'\n },\n renewAll: {\n method: 'POST',\n url: '/api/item-list/?op=renew_all'\n },\n remindAll: {\n method: 'POST',\n url: '/api/item-list/?op=remind_all'\n },\n setStatusAll: {\n method: 'POST',\n url: '/api/item-list/?op=set_status_all'\n },\n export: {\n method: 'POST',\n url: '/api/item-list/:id/?op=export',\n },\n checkHoldings: {\n method: 'POST',\n url: '/api/item-list/:id?op=holdings'\n },\n checkHoldingsFor: {\n method: 'POST',\n url: '/api/item-list/:id?op=holdings',\n isArray: true,\n },\n commit: {\n method: 'POST',\n url: '/api/item-list/:id?op=commit'\n },\n addToPortalCart: {\n method: 'POST',\n url: '/api/item-list/?op=add-to-portal-cart'\n },\n goToPortalCart: {\n method: 'POST',\n url: '/api/item-list/?op=go-to-portal-cart'\n },\n getPortalCart: {\n method: 'GET',\n url: '/api/item-list/?view=portal-cart'\n },\n getPortalRoutes: {\n method: 'GET',\n url: '/api/item-list/?view=portal-routes'\n },\n });\n}])\n\n/*\n// TODO - This should be limited to only applicable selectors\n// maybe add a permit=X function and make sure we also have X?\n.factory('patronsModel', function($resource) {\n return $resource('/api/patron/:id', {}, {\n getBranchPatrons: {\n method: 'GET',\n url: '/api/patron/?view=picker&scope=branch&permit=:permit',\n isArray: true\n },\n getSuEligiblePatrons: {\n method: 'GET',\n url: '/api/patron/?view=picker-extended&subpermissions=1',\n isArray: true\n },\n generateSuTicket: {\n method: 'POST',\n url: '/api/patron/:id?op=generate-su-ticket',\n },\n });\n})\n*/\n\n;\n\n})();\n"],"x_google_ignoreList":[]}